@uipath/flow-tool 1.201.0 → 1.202.0-preview.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{authoring-7r6g06c7.js → authoring-wpa6d0vf.js} +74 -48
- package/dist/binding-service-dh91hw9e.js +141 -0
- package/dist/{browser-strategy-rkcvc6tn.js → browser-strategy-6atc7v8s.js} +3 -3
- package/dist/connector-service-d9ph9kx1.js +948 -0
- package/dist/{conversion-H2VEEYLB-ng48mbf7.js → conversion-QANACVCM-zs0hhfjv.js} +39 -33
- package/dist/{de-5KVP66U6-f7knj511.js → de-5KVP66U6-1bnf4zt5.js} +52 -52
- package/dist/de-6U7ECGHH-xwsw33fk.js +35 -0
- package/dist/{de-5HU6GFWV-4EZK6G7N-yrh21f52.js → de-7XQXX2DK-IDWR6CAR-n4z245kt.js} +857 -845
- package/dist/{de-5HU6GFWV-zheyw6d2.js → de-7XQXX2DK-tqdy5n2b.js} +857 -845
- package/dist/{de-HKTOVV4H-d5yw5j21.js → de-HKTOVV4H-pe9w0xn8.js} +46 -46
- package/dist/{de-XLT4QPIP-0rtw349w.js → de-XLT4QPIP-kj81dxjc.js} +9 -9
- package/dist/debug-conversation-trigger-452frzyz.js +41 -0
- package/dist/debug-http-client-wd6xb8v4.js +31 -0
- package/dist/debug-service-fvyexnb1.js +58 -0
- package/dist/edge-service-e77bndbe.js +162 -0
- package/dist/{elk.bundled-pydbzeez.js → elk.bundled-vpsbxz0p.js} +3 -3
- package/dist/{en-MABQK6C4-REWE2WG2-8dp4cqdg.js → en-5ZGC5VQ3-EWX7ILY7-rmtj70fk.js} +898 -898
- package/dist/{en-MABQK6C4-nkzq2j8t.js → en-5ZGC5VQ3-h581y30k.js} +897 -897
- package/dist/en-EPWMLP2N-2wvge238.js +28 -0
- package/dist/{en-UNT7BE26-rhh65jry.js → en-LQHKFSEY-vvbe8ve9.js} +13 -11
- package/dist/{en-P4AS26D7-szrzfxz1.js → en-P4AS26D7-zptp77bm.js} +45 -45
- package/dist/{en-4KXU7N7B-66fntxa2.js → en-X3S6IZ5J-nhkg6pwn.js} +54 -60
- package/dist/{es-MX-H2EAXLYI-qwnqt48x.js → es-MX-H2EAXLYI-j42kfkxy.js} +46 -46
- package/dist/es-MX-NHTIP5YM-ar6zfx95.js +35 -0
- package/dist/{es-MX-OKNDG2NH-c1k1s1tg.js → es-MX-OKNDG2NH-tcac6j4g.js} +9 -9
- package/dist/{es-MX-POESXYP2-UW6VP5QI-cd3a79g9.js → es-MX-U2VN3SZW-KYAJ2ONK-kdram1g1.js} +857 -845
- package/dist/{es-MX-POESXYP2-9538722k.js → es-MX-U2VN3SZW-anynsmd8.js} +857 -845
- package/dist/{es-MX-XAVLOQW3-9b3cp9n8.js → es-MX-XAVLOQW3-q00km2y7.js} +52 -52
- package/dist/{es-NQICVYSQ-aa9z3nhs.js → es-NQICVYSQ-3dtqgget.js} +9 -9
- package/dist/{es-O4PAV2CR-jwfaawwk.js → es-O4PAV2CR-0g23ve3v.js} +46 -46
- package/dist/es-PQ3RA33Q-y4kxhjfr.js +35 -0
- package/dist/{es-VFPNVMP5-thm0z9sr.js → es-VFPNVMP5-bx2c337j.js} +52 -52
- package/dist/{es-LUTYN7U7-IY5QWOUF-k2aefzc0.js → es-ZGCPPOME-24PJEGPV-rn86z9kn.js} +857 -845
- package/dist/{es-LUTYN7U7-ye5zn4sn.js → es-ZGCPPOME-8gazspjj.js} +857 -845
- package/dist/flow-api-wv8p8xbd.js +36 -0
- package/dist/flow-debug-service-tfbefas6.js +741 -0
- package/dist/flow-eval-node-target-c6wea7rz.js +44 -0
- package/dist/flow-eval-run-service-01kdky72.js +961 -0
- package/dist/flow-eval-service-ft8e16jz.js +1338 -0
- package/dist/flow-init-service-mva4crty.js +35 -0
- package/dist/flow-io-r318sjrw.js +36 -0
- package/dist/flow-migrate-service-rwcdx60q.js +119 -0
- package/dist/flow-pack-service-p4hxyj9e.js +280 -0
- package/dist/flow-validate-service-j25pxpwz.js +57 -0
- package/dist/{fr-5YRGHXF5-q6b63yg0.js → fr-5YRGHXF5-kz1ywpv6.js} +46 -46
- package/dist/{fr-E5MDO4EG-exvrs7ge.js → fr-E5MDO4EG-c3mkty2t.js} +52 -52
- package/dist/{fr-PFHIL7YZ-cxnshwa8.js → fr-PFHIL7YZ-2n589p47.js} +9 -9
- package/dist/fr-R4L3OAWI-30khpsmn.js +35 -0
- package/dist/{fr-LJIK56LW-STG6QR3T-cextvv8q.js → fr-T3G5GZCT-TU4IKBEG-k6xz1s1f.js} +857 -845
- package/dist/{fr-LJIK56LW-xf3xf2kh.js → fr-T3G5GZCT-y3f7xb1q.js} +857 -845
- package/dist/governance-fetcher-j9cx4f1d.js +391 -0
- package/dist/headless-h1myzz32.js +177196 -0
- package/dist/hitl-service-kvt5wms4.js +256 -0
- package/dist/index-1kgxzh8x.js +143 -0
- package/dist/index-4zrwgb3c.js +111 -0
- package/dist/index-ev4mdg5y.js +4801 -0
- package/dist/{index-er6vy3jk.js → index-eybzsdcc.js} +295 -317
- package/dist/{index-hsadteg4.js → index-h8cph72k.js} +4 -4
- package/dist/{index-p50kx6jv.js → index-k5tbd184.js} +276 -276
- package/dist/index-n33np76r.js +630 -0
- package/dist/{index-9f8q2ege.js → index-p3vk57z7.js} +27 -27
- package/dist/{index-dhcd9b82.js → index-w0z4s69j.js} +166 -81
- package/dist/index.js +29 -32
- package/dist/{index.umd.min-drpc57vn.js → index.umd.min-fqefd9kn.js} +3 -3
- package/dist/init.js +24 -25
- package/dist/input-reader-6pwhtp8v.js +11 -0
- package/dist/{ja-FGEGLBJ3-w9byjhze.js → ja-BI5K3JDZ-77zat4z2.js} +857 -845
- package/dist/{ja-FGEGLBJ3-MG3BSWXD-vq19zt2p.js → ja-BI5K3JDZ-GTIHISTZ-s6n3r2ab.js} +857 -845
- package/dist/{ja-DPO5PPLO-s2a7j3nj.js → ja-DPO5PPLO-mysfmznx.js} +52 -52
- package/dist/{ja-P4OSE367-8ywxep9a.js → ja-P4OSE367-srr3qypg.js} +9 -9
- package/dist/{ja-SDAPAVCR-943p5gx7.js → ja-SDAPAVCR-kg39dfap.js} +46 -46
- package/dist/ja-ZHKCBE5W-x73w6x3e.js +35 -0
- package/dist/{js-yaml-cggj6w7q.js → js-yaml-87cdx6bm.js} +27 -19
- package/dist/known-types-a20evmmg.js +20 -0
- package/dist/ko-6W7OGLLB-mnmqc584.js +35 -0
- package/dist/{ko-ITOPNIYI-qz39kamf.js → ko-ITOPNIYI-hmxd2asd.js} +52 -52
- package/dist/{ko-TUAK3G3A-s25w0ckg.js → ko-JOBF5V7N-4bv0mjje.js} +857 -845
- package/dist/{ko-TUAK3G3A-TBEBKOTM-ns78t2rv.js → ko-JOBF5V7N-O7T4HKIE-ape840y0.js} +857 -845
- package/dist/{ko-NAGOZLC3-wb3y9yhh.js → ko-NAGOZLC3-gaxxsnd2.js} +46 -46
- package/dist/{ko-PYOS2KJP-5sbj5bp8.js → ko-PYOS2KJP-e7dshyxy.js} +9 -9
- package/dist/localization-h8a53z6p.js +47 -0
- package/dist/{node-gd4469ea.js → node-cnrcrrb0.js} +7973 -7166
- package/dist/node-service-8g5x5d2y.js +70 -0
- package/dist/{node-strategy-8gzp67xs.js → node-strategy-gc4sg6a2.js} +132 -16
- package/dist/node-sync-service-e2reh33c.js +42 -0
- package/dist/{packager-tool-9qecd4wb.js → packager-tool-0gctz400.js} +5 -2
- package/dist/{packager-tool-1cb0d5e0.js → packager-tool-0v3s8r01.js} +2 -2
- package/dist/{packager-tool-wckvcay0.js → packager-tool-1yh8g550.js} +9 -7
- package/dist/packager-tool-27avrkyk.js +15 -0
- package/dist/packager-tool-2wefykg9.js +106 -0
- package/dist/packager-tool-4fhgws0x.js +757 -0
- package/dist/{packager-tool-7yjpwj92.js → packager-tool-4hx14snw.js} +49 -48
- package/dist/{packager-tool-3wygxefb.js → packager-tool-58q3tvh7.js} +10 -10
- package/dist/{packager-tool-cvwr40ax.js → packager-tool-5qaxht3k.js} +1425 -550
- package/dist/packager-tool-5z1eg3ea.js +33289 -0
- package/dist/{packager-tool-17cn3xb7.js → packager-tool-6m078z5s.js} +13388 -19366
- package/dist/packager-tool-7b6tpg7y.js +77 -0
- package/dist/packager-tool-7exrv6w7.js +23 -0
- package/dist/packager-tool-8dszjcg5.js +4892 -0
- package/dist/packager-tool-8j92afwp.js +58 -0
- package/dist/packager-tool-94w17vf8.js +85 -0
- package/dist/{packager-tool-vdd02vns.js → packager-tool-9bec251q.js} +543 -1493
- package/dist/packager-tool-a1hbfmws.js +399 -0
- package/dist/{packager-tool-tq9q9m77.js → packager-tool-a42jnpy5.js} +3 -9
- package/dist/packager-tool-abbtfk2v.js +54 -0
- package/dist/packager-tool-cdwpc9fh.js +4945 -0
- package/dist/packager-tool-e0p2zsyc.js +97 -0
- package/dist/packager-tool-eebq62fq.js +540 -0
- package/dist/{packager-tool-m6x5sczh.js → packager-tool-em5r6g6j.js} +14 -21
- package/dist/packager-tool-eypvc2kv.js +23 -0
- package/dist/packager-tool-f0kd5kq4.js +2327 -0
- package/dist/packager-tool-fjaa8d3v.js +289 -0
- package/dist/packager-tool-g0eha8ge.js +121 -0
- package/dist/packager-tool-g9y14b10.js +54 -0
- package/dist/packager-tool-gbgfwx8f.js +2 -0
- package/dist/packager-tool-jc34wvrw.js +64 -0
- package/dist/packager-tool-k99nf3pq.js +25 -0
- package/dist/{packager-tool-hr0xg6q8.js → packager-tool-kn91khp6.js} +17 -13
- package/dist/{packager-tool-jysvpt8p.js → packager-tool-m3wkcxf5.js} +2 -2
- package/dist/{packager-tool-78a3szsp.js → packager-tool-m5qfverj.js} +1720 -2014
- package/dist/packager-tool-mwvbq6ef.js +79 -0
- package/dist/packager-tool-nq4rcqqg.js +87 -0
- package/dist/packager-tool-nxx8naxv.js +4234 -0
- package/dist/{packager-tool-3y0k15sv.js → packager-tool-p9stswjj.js} +2858 -19342
- package/dist/packager-tool-r0xpcqwg.js +188 -0
- package/dist/packager-tool-raq2wbwp.js +18 -0
- package/dist/{packager-tool-y3dezh8k.js → packager-tool-rs6jm14d.js} +6 -4
- package/dist/packager-tool-s9ff3kk4.js +6 -0
- package/dist/{packager-tool-pbsghbmj.js → packager-tool-sykgshez.js} +2 -2
- package/dist/packager-tool-tf3d2y8z.js +31 -0
- package/dist/packager-tool-tn5e7wnb.js +384 -0
- package/dist/packager-tool-v2rkcehq.js +457 -0
- package/dist/packager-tool-v86fdwvs.js +262 -0
- package/dist/packager-tool-v9psxdy5.js +8185 -0
- package/dist/packager-tool-v9rrz038.js +602 -0
- package/dist/packager-tool-vpqyvh4c.js +304 -0
- package/dist/packager-tool-wcha0e5r.js +443 -0
- package/dist/{packager-tool-1gt6js73.js → packager-tool-xhzfwa5p.js} +6 -4
- package/dist/{packager-tool-5arsyj36.js → packager-tool-yc4kw997.js} +4 -2
- package/dist/{packager-tool-pkq70dec.js → packager-tool-ywqc9rtf.js} +31 -3
- package/dist/{packager-tool-0567pxpm.js → packager-tool-z9bfhmm5.js} +10 -10
- package/dist/{packager-tool-gmrwvnyh.js → packager-tool-zfrp8ecz.js} +8 -58
- package/dist/{packager-tool-3vdpx9ng.js → packager-tool-zke08y39.js} +1121 -1846
- package/dist/packager-tool.js +17 -17
- package/dist/{pt-7TR74ZMB-1g8my56g.js → pt-7TR74ZMB-bmphh4x2.js} +46 -46
- package/dist/{pt-BR-7XGDXRDJ-TR5D7JYL-533wvy7p.js → pt-BR-7DYX2YPT-LZYH4YFD-bx43vx3z.js} +857 -845
- package/dist/{pt-BR-7XGDXRDJ-espnkgrj.js → pt-BR-7DYX2YPT-fqxqzqtf.js} +857 -845
- package/dist/{pt-BR-BXURWX4W-dqr4dnf9.js → pt-BR-BXURWX4W-bwfcwhk1.js} +52 -52
- package/dist/{pt-BR-DXXSYJDO-bn84ewp7.js → pt-BR-DXXSYJDO-a2tp13v7.js} +46 -46
- package/dist/pt-BR-UQCYHKMJ-q78n7q70.js +35 -0
- package/dist/{pt-BR-Z3TUDJHM-n7ngej6s.js → pt-BR-Z3TUDJHM-8v73x9s7.js} +9 -9
- package/dist/pt-D6A6MVB2-yvxca8vz.js +35 -0
- package/dist/{pt-TMVOOITK-myha14p9.js → pt-HTXGGX6Y-97t21dty.js} +857 -845
- package/dist/{pt-TMVOOITK-H2ADYHSD-ezpggv33.js → pt-HTXGGX6Y-ZHQSDFKI-vehfxadc.js} +857 -845
- package/dist/{pt-KSBWHAVQ-hd52bpsz.js → pt-KSBWHAVQ-mhvhgr2p.js} +9 -9
- package/dist/{pt-YI4VE4YQ-v7gjeg82.js → pt-YI4VE4YQ-wdkn2dcf.js} +52 -52
- package/dist/ro-4TMBHS5M-jz0anpt6.js +35 -0
- package/dist/{ro-BJFER5OI-334n28rj.js → ro-BJFER5OI-83tq8vxh.js} +46 -46
- package/dist/{ro-2VALF3S5-NXBJUEZN-nqw1zs33.js → ro-KTNQ3MNU-XIE6YPQN-gen8p82z.js} +857 -845
- package/dist/{ro-2VALF3S5-cdz5afkr.js → ro-KTNQ3MNU-y08n1x5g.js} +857 -845
- package/dist/{ro-RKXZUG7Z-cm3xn3fj.js → ro-RKXZUG7Z-nndrrsq7.js} +52 -52
- package/dist/{ro-VZUZTAMR-tccygznf.js → ro-VZUZTAMR-f4q0g3em.js} +9 -9
- package/dist/{ru-GSCT4ARZ-qj8dm4dp.js → ru-GSCT4ARZ-1sa9wbr8.js} +1 -1
- package/dist/{ru-GSCT4ARZ-FTVCGQTL-5nh4jf2e.js → ru-GSCT4ARZ-FTVCGQTL-gee86v14.js} +1 -1
- package/dist/{ru-T73OPRTC-mp5gn7gs.js → ru-T73OPRTC-4knxw497.js} +1 -1
- package/dist/{ru-WBLQ5B2U-awt245ky.js → ru-WBLQ5B2U-8r3c7v6d.js} +1 -1
- package/dist/ru-WBLQ5B2U-kdey8qa4.js +10 -0
- package/dist/{ru-WBLQ5B2U-57ezmxzz.js → ru-WBLQ5B2U-wemdzdqd.js} +1 -1
- package/dist/{serialization-GAW7UFOK-9hkdmmd2.js → serialization-J5QK3HPY-bgewpt6f.js} +158 -152
- package/dist/services/expression-validator.d.ts +28 -0
- package/dist/services/flow-eval-schema/types.d.ts +17 -0
- package/dist/services/flow-validate-service.d.ts +26 -0
- package/dist/services/node-validators/js-expression-syntax-validator.d.ts +6 -3
- package/dist/services/tidy-service.d.ts +26 -0
- package/dist/services/vars-reference-rule.d.ts +156 -0
- package/dist/shared-utils-8xdq6f04.js +20 -0
- package/dist/tidy-service-ashgyfwj.js +34 -0
- package/dist/tool.js +30 -33
- package/dist/{tr-AA6O2WSR-ga1xnymp.js → tr-AA6O2WSR-86ba4bdf.js} +52 -52
- package/dist/tr-GSQQ6SOT-hv1gyeaa.js +35 -0
- package/dist/{tr-RQTZ2DMS-bpgd4kma.js → tr-RQTZ2DMS-w3cwv8s4.js} +46 -46
- package/dist/{tr-W4AVRSQ4-bt3qqqdy.js → tr-W4AVRSQ4-hjr81xzk.js} +9 -9
- package/dist/{tr-RIFCEXFH-BZS7S7QK-q465b62g.js → tr-XHSSPNK7-GW5KVXR3-2w3f1ss6.js} +857 -845
- package/dist/{tr-RIFCEXFH-hgkb4g8y.js → tr-XHSSPNK7-cmqyc4wd.js} +857 -845
- package/dist/utils/handle-expansion.d.ts +16 -0
- package/dist/validation.js +47 -27
- package/dist/variable-service-er7ph4br.js +152 -0
- package/dist/wait-for-flow-job-45p1a444.js +134 -0
- package/dist/{zh-CN-IP3FMXWM-H3JI2XUM-ta4yac7h.js → zh-CN-2EHQT23P-T5XRQCVS-ac17dpc1.js} +857 -845
- package/dist/{zh-CN-IP3FMXWM-1cfarhcp.js → zh-CN-2EHQT23P-mr63c4xz.js} +857 -845
- package/dist/{zh-CN-6YRAN57T-j7jp34ws.js → zh-CN-6YRAN57T-rd7x2tqs.js} +9 -9
- package/dist/zh-CN-EDFRBU32-vvj7tmaq.js +35 -0
- package/dist/{zh-CN-R2KLHXQV-wttmznep.js → zh-CN-R2KLHXQV-agqr45ds.js} +52 -52
- package/dist/{zh-CN-ZC6LT4IN-vty9mvv8.js → zh-CN-ZC6LT4IN-kbzvvzz5.js} +46 -46
- package/dist/zh-TW-HAICM6DE-rh7k3cq7.js +35 -0
- package/dist/{zh-TW-AGIDXIPF-R4XZRDJZ-cgxxz5k4.js → zh-TW-MGZCE5BT-P6FLIUAR-zmq07vhs.js} +857 -845
- package/dist/{zh-TW-AGIDXIPF-n6r6w7vy.js → zh-TW-MGZCE5BT-c4ekmrz9.js} +857 -845
- package/dist/{zh-TW-R7Y5KVMU-q81f4z2p.js → zh-TW-R7Y5KVMU-m61y07e3.js} +46 -46
- package/dist/{zh-TW-RJGCQANM-gqn0aca0.js → zh-TW-RJGCQANM-w0x3qc1p.js} +52 -52
- package/dist/{zh-TW-SO2245GN-m5kh88bg.js → zh-TW-SO2245GN-n2bn1gg4.js} +9 -9
- package/package.json +2 -2
- package/dist/de-5KVP66U6-t37qr521.js +0 -161
- package/dist/de-WMKOGI7B-6dc1zdnp.js +0 -143
- package/dist/en-4KXU7N7B-24v330b5.js +0 -118
- package/dist/en-X4DLPKLJ-1h9gj5c1.js +0 -110
- package/dist/es-BVPGJUWA-0k59eqfv.js +0 -143
- package/dist/es-MX-U6YL5KQX-k4qvpgd4.js +0 -143
- package/dist/es-MX-XAVLOQW3-47a405kf.js +0 -161
- package/dist/es-VFPNVMP5-6sftsdxe.js +0 -161
- package/dist/fr-E5MDO4EG-f3x1jeqw.js +0 -161
- package/dist/fr-EKTTDR7Z-m2t68j0m.js +0 -143
- package/dist/ja-DPO5PPLO-fmbg7zt2.js +0 -161
- package/dist/ja-Y2YBTXNN-5sszk2zv.js +0 -143
- package/dist/ko-ITOPNIYI-aqy82jk8.js +0 -161
- package/dist/ko-S33XAV26-rfsv9myd.js +0 -143
- package/dist/packager-tool-0sz8se22.js +0 -10671
- package/dist/packager-tool-8mqydhz8.js +0 -9850
- package/dist/packager-tool-p9j43p8h.js +0 -115
- package/dist/packager-tool-v74yk918.js +0 -107
- package/dist/packager-tool-xj0agqpk.js +0 -11
- package/dist/pt-BR-BXURWX4W-xzyndmrx.js +0 -161
- package/dist/pt-BR-JO45SCNI-mt93s33c.js +0 -143
- package/dist/pt-W3NA4LIZ-fwd1q4j0.js +0 -143
- package/dist/pt-YI4VE4YQ-521681zf.js +0 -161
- package/dist/ro-RKXZUG7Z-pdztdafr.js +0 -161
- package/dist/ro-WLKX6HF5-b9tj8q67.js +0 -143
- package/dist/ru-WBLQ5B2U-8xd4gpv2.js +0 -10
- package/dist/ru-WBLQ5B2U-mswqs9wc.js +0 -10
- package/dist/tr-3U2W2XCA-43ptfc0m.js +0 -143
- package/dist/tr-AA6O2WSR-bj9n80k8.js +0 -161
- package/dist/zh-CN-IPZYQR3P-egjmvyjf.js +0 -143
- package/dist/zh-CN-R2KLHXQV-x6gnk2n0.js +0 -161
- package/dist/zh-TW-RJGCQANM-tt8j5wv2.js +0 -161
- package/dist/zh-TW-YPVSQVGT-b378ztph.js +0 -143
|
@@ -1,187 +1,377 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "./packager-tool-
|
|
2
|
+
UI_ONLY_NODE_TYPES
|
|
3
|
+
} from "./packager-tool-4fhgws0x.js";
|
|
4
|
+
import {
|
|
5
|
+
checkForwardMigration
|
|
6
|
+
} from "./packager-tool-mwvbq6ef.js";
|
|
7
|
+
import {
|
|
8
|
+
crossNodeBindingValidator,
|
|
9
|
+
isConnectorTriggerType,
|
|
10
|
+
isConnectorWaitForEventType,
|
|
11
|
+
isEventDrivenConnectorType
|
|
12
|
+
} from "./packager-tool-2wefykg9.js";
|
|
13
|
+
import {
|
|
14
|
+
ERROR_SOURCE_PORT,
|
|
15
|
+
deriveOutcomeHandleIds,
|
|
16
|
+
expandHandleIds,
|
|
17
|
+
isHandleVisible,
|
|
18
|
+
isTemplatedHandleId,
|
|
19
|
+
parseVisibleInputKey,
|
|
20
|
+
portMatchesTemplate
|
|
21
|
+
} from "./packager-tool-nq4rcqqg.js";
|
|
22
|
+
import {
|
|
23
|
+
isRecord
|
|
24
|
+
} from "./packager-tool-8j92afwp.js";
|
|
25
|
+
import {
|
|
26
|
+
collectNodeBindingResourceKeys,
|
|
27
|
+
isProcessNode,
|
|
28
|
+
resolveProcessTarget
|
|
29
|
+
} from "./packager-tool-v2rkcehq.js";
|
|
30
|
+
import {
|
|
31
|
+
loadRegistry
|
|
32
|
+
} from "./packager-tool-cdwpc9fh.js";
|
|
33
|
+
import {
|
|
34
|
+
validateIntSvcNode
|
|
35
|
+
} from "./packager-tool-v9psxdy5.js";
|
|
36
|
+
import {
|
|
37
|
+
getLoginStatusAsync
|
|
38
|
+
} from "./packager-tool-f0kd5kq4.js";
|
|
13
39
|
import {
|
|
14
40
|
findNearestParentUipxFile,
|
|
15
|
-
getLoginStatusAsync,
|
|
16
41
|
readSolutionManifest
|
|
17
|
-
} from "./packager-tool-
|
|
42
|
+
} from "./packager-tool-eebq62fq.js";
|
|
43
|
+
import {
|
|
44
|
+
resolveProjectFlowFile
|
|
45
|
+
} from "./packager-tool-94w17vf8.js";
|
|
46
|
+
import {
|
|
47
|
+
canonicalAgentInputPath,
|
|
48
|
+
collectAgentInputLeaves,
|
|
49
|
+
createFlowRefResolver,
|
|
50
|
+
resolveWorkflowRefs
|
|
51
|
+
} from "./packager-tool-6m078z5s.js";
|
|
18
52
|
import {
|
|
19
53
|
OutputFormatter,
|
|
20
54
|
RESULTS,
|
|
21
55
|
catchError,
|
|
56
|
+
extractErrorMessageSync,
|
|
22
57
|
logger
|
|
23
|
-
} from "./packager-tool-
|
|
58
|
+
} from "./packager-tool-5qaxht3k.js";
|
|
24
59
|
import {
|
|
25
60
|
getFileSystem
|
|
26
|
-
} from "./packager-tool-
|
|
61
|
+
} from "./packager-tool-0v3s8r01.js";
|
|
27
62
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
createBinding,
|
|
32
|
-
createFlowRefResolver,
|
|
33
|
-
generateNodeIdAndName,
|
|
34
|
-
getMigrationChain,
|
|
35
|
-
isInlineAgentNodeType,
|
|
36
|
-
migrateWorkflowToFloor,
|
|
37
|
-
minimumSupportedVersion,
|
|
38
|
-
operateRuntimeOptions,
|
|
39
|
-
readInlineAgentSource,
|
|
40
|
-
registerFlowMigrationsI18n,
|
|
41
|
-
resolveWorkflowRefs,
|
|
42
|
-
runtimeMigrations,
|
|
43
|
-
runtimeVersion
|
|
44
|
-
} from "./packager-tool-17cn3xb7.js";
|
|
63
|
+
ManifestClient,
|
|
64
|
+
buildErrorHandle
|
|
65
|
+
} from "./packager-tool-p9stswjj.js";
|
|
45
66
|
import {
|
|
46
67
|
AGENT_NODE_TYPES,
|
|
47
|
-
EntryPointType,
|
|
48
|
-
ProjectType,
|
|
49
68
|
cliRules,
|
|
50
69
|
conditionExpressionRule,
|
|
51
70
|
decodeLiteral,
|
|
52
|
-
generateBindingsJson,
|
|
53
|
-
generateEntryPointsJson,
|
|
54
|
-
generateOperateJson,
|
|
55
|
-
getBindingResources,
|
|
56
|
-
getEntryPoints,
|
|
57
71
|
getJsExpressionBody,
|
|
58
72
|
instance,
|
|
59
73
|
isBlockingSeverity,
|
|
60
|
-
isConversationalOrVoiceAgentNodeType,
|
|
61
74
|
isExpressionValue,
|
|
62
75
|
isStartEventNode,
|
|
63
76
|
isVoiceAgentNodeType,
|
|
64
|
-
mergeBindingResources,
|
|
65
77
|
parseWorkflowJson,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
} from "./packager-tool-
|
|
78
|
+
runRules,
|
|
79
|
+
toSerializedNodes
|
|
80
|
+
} from "./packager-tool-m5qfverj.js";
|
|
81
|
+
import {
|
|
82
|
+
registerFlowSchemaI18n
|
|
83
|
+
} from "./packager-tool-e0p2zsyc.js";
|
|
84
|
+
import {
|
|
85
|
+
__require
|
|
86
|
+
} from "./packager-tool-1yh8g550.js";
|
|
69
87
|
|
|
70
|
-
// src/services/connector-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
// src/services/connector-binding-validator.ts
|
|
89
|
+
var STATIC_CONNECTOR_KEYWORDS = [
|
|
90
|
+
"slack",
|
|
91
|
+
"gmail",
|
|
92
|
+
"outlook",
|
|
93
|
+
"office365",
|
|
94
|
+
"exchange",
|
|
95
|
+
"jira",
|
|
96
|
+
"confluence",
|
|
97
|
+
"salesforce",
|
|
98
|
+
"sfdc",
|
|
99
|
+
"hubspot",
|
|
100
|
+
"servicenow",
|
|
101
|
+
"zendesk",
|
|
102
|
+
"intercom",
|
|
103
|
+
"notion",
|
|
104
|
+
"asana",
|
|
105
|
+
"trello",
|
|
106
|
+
"monday",
|
|
107
|
+
"github",
|
|
108
|
+
"gitlab",
|
|
109
|
+
"bitbucket",
|
|
110
|
+
"discord",
|
|
111
|
+
"teams",
|
|
112
|
+
"sharepoint",
|
|
113
|
+
"onedrive",
|
|
114
|
+
"dropbox",
|
|
115
|
+
"box",
|
|
116
|
+
"googledrive",
|
|
117
|
+
"docusign",
|
|
118
|
+
"adobesign",
|
|
119
|
+
"mailchimp",
|
|
120
|
+
"sendgrid",
|
|
121
|
+
"twilio",
|
|
122
|
+
"stripe",
|
|
123
|
+
"shopify",
|
|
124
|
+
"quickbooks",
|
|
125
|
+
"xero",
|
|
126
|
+
"workday",
|
|
127
|
+
"successfactors",
|
|
128
|
+
"bamboohr",
|
|
129
|
+
"sap",
|
|
130
|
+
"netsuite",
|
|
131
|
+
"oracle",
|
|
132
|
+
"marketo",
|
|
133
|
+
"pardot",
|
|
134
|
+
"segment",
|
|
135
|
+
"snowflake",
|
|
136
|
+
"bigquery",
|
|
137
|
+
"redshift",
|
|
138
|
+
"databricks",
|
|
139
|
+
"tableau",
|
|
140
|
+
"powerbi"
|
|
141
|
+
];
|
|
142
|
+
var ACTION_VERBS = [
|
|
143
|
+
"send",
|
|
144
|
+
"post",
|
|
145
|
+
"create",
|
|
146
|
+
"update",
|
|
147
|
+
"delete",
|
|
148
|
+
"get",
|
|
149
|
+
"fetch",
|
|
150
|
+
"list",
|
|
151
|
+
"read",
|
|
152
|
+
"write",
|
|
153
|
+
"upload",
|
|
154
|
+
"download",
|
|
155
|
+
"find",
|
|
156
|
+
"add",
|
|
157
|
+
"remove",
|
|
158
|
+
"query",
|
|
159
|
+
"search",
|
|
160
|
+
"notify",
|
|
161
|
+
"publish",
|
|
162
|
+
"subscribe",
|
|
163
|
+
"trigger",
|
|
164
|
+
"watch",
|
|
165
|
+
"share",
|
|
166
|
+
"invite",
|
|
167
|
+
"schedule",
|
|
168
|
+
"assign",
|
|
169
|
+
"close",
|
|
170
|
+
"open",
|
|
171
|
+
"reply",
|
|
172
|
+
"forward",
|
|
173
|
+
"archive"
|
|
174
|
+
];
|
|
175
|
+
var MANAGED_HTTP_NODE_TYPE = "core.action.http.v2";
|
|
176
|
+
async function loadConnectorCatalog() {
|
|
177
|
+
const keywords = new Set(STATIC_CONNECTOR_KEYWORDS);
|
|
178
|
+
const activityTokenBags = [];
|
|
179
|
+
const [err, registry] = await catchError(loadRegistry());
|
|
180
|
+
if (err) {
|
|
181
|
+
logger.debug(`Connector-binding validator: could not load IS catalog cache (${err.message}); falling back to static keyword list.`);
|
|
182
|
+
return { keywords, activityTokenBags };
|
|
183
|
+
}
|
|
184
|
+
if (!registry) {
|
|
185
|
+
return { keywords, activityTokenBags };
|
|
186
|
+
}
|
|
187
|
+
for (const connector of registry.discovered.connectors) {
|
|
188
|
+
addConnectorKeywords(connector, keywords);
|
|
189
|
+
for (const activity of connector.activities) {
|
|
190
|
+
const tokens = tokenize(activity.displayName);
|
|
191
|
+
if (tokens.size > 0) {
|
|
192
|
+
activityTokenBags.push({
|
|
193
|
+
connectorKey: connector.key,
|
|
194
|
+
connectorName: connector.name,
|
|
195
|
+
tokens
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { keywords, activityTokenBags };
|
|
73
201
|
}
|
|
74
|
-
function
|
|
75
|
-
|
|
202
|
+
function addConnectorKeywords(connector, out) {
|
|
203
|
+
const keyParts = connector.key.toLowerCase().split("-").filter((p) => p && p !== "uipath");
|
|
204
|
+
for (const part of keyParts)
|
|
205
|
+
out.add(part);
|
|
206
|
+
for (const t of tokenize(connector.name))
|
|
207
|
+
out.add(t);
|
|
76
208
|
}
|
|
77
|
-
function
|
|
78
|
-
|
|
209
|
+
function tokenize(label) {
|
|
210
|
+
if (!label)
|
|
211
|
+
return new Set;
|
|
212
|
+
const tokens = label.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1);
|
|
213
|
+
return new Set(tokens);
|
|
79
214
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
appliesTo: (node) => node.type?.startsWith("uipath.connector") ?? false,
|
|
85
|
-
validate: (node) => {
|
|
86
|
-
const rawDetail = node.inputs?.detail;
|
|
87
|
-
if (!rawDetail || typeof rawDetail !== "object" || Array.isArray(rawDetail)) {
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
|
-
const detail = rawDetail;
|
|
91
|
-
if (Object.keys(detail).length === 0)
|
|
92
|
-
return [];
|
|
93
|
-
const isEventDriven = isEventDrivenConnectorType(node.type);
|
|
94
|
-
const context = {
|
|
95
|
-
nodeId: node.id,
|
|
96
|
-
nodeKind: isEventDriven ? "trigger" : "activity",
|
|
97
|
-
connectionId: detail.connectionId,
|
|
98
|
-
folderKey: detail.connectionFolderKey,
|
|
99
|
-
objectName: detail.objectName ?? detail.connector,
|
|
100
|
-
httpMethod: detail.method,
|
|
101
|
-
endpoint: detail.endpoint,
|
|
102
|
-
eventMode: detail.eventMode,
|
|
103
|
-
eventType: detail.eventType,
|
|
104
|
-
bodyParameters: detail.bodyParameters,
|
|
105
|
-
queryParameters: detail.queryParameters,
|
|
106
|
-
pathParameters: detail.pathParameters,
|
|
107
|
-
eventParameters: detail.eventParameters,
|
|
108
|
-
configuration: detail.configuration
|
|
109
|
-
};
|
|
110
|
-
const issues = validateIntSvcNode(context).map((issue) => ({
|
|
111
|
-
path: `nodes[${node.id}].inputs.detail`,
|
|
112
|
-
message: issue.title,
|
|
113
|
-
severity: issue.type === "Error" ? "error" : "warning"
|
|
114
|
-
}));
|
|
115
|
-
issues.push(...validateCeqlFilterShape(node.id, detail));
|
|
116
|
-
return issues;
|
|
215
|
+
function readNodeLabel(node) {
|
|
216
|
+
const fromDisplay = node.display?.label;
|
|
217
|
+
if (typeof fromDisplay === "string" && fromDisplay.length > 0) {
|
|
218
|
+
return fromDisplay;
|
|
117
219
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const queryParameters = readObjectField(detail, "queryParameters");
|
|
122
|
-
if (queryParameters && Object.hasOwn(queryParameters, "$where")) {
|
|
123
|
-
issues.push({
|
|
124
|
-
path: `nodes[${nodeId}].inputs.detail.queryParameters["$where"]`,
|
|
125
|
-
severity: "error",
|
|
126
|
-
message: 'Connector node uses OData-style `"$where"` (with leading `$`) ' + "in queryParameters. CEQL uses bare `where` — rename the key " + 'from `"$where"` to `"where"`. The leading `$` is the OData ' + "system-query-option prefix and is not recognized by " + "uipath.connector.* nodes. See uipath-maestro-flow skill — " + 'plugins/connector/impl.md, "Hand-authored CEQL strings" ' + "(MST-9265)."
|
|
127
|
-
});
|
|
220
|
+
const fromName = node.name;
|
|
221
|
+
if (typeof fromName === "string" && fromName.length > 0) {
|
|
222
|
+
return fromName;
|
|
128
223
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
issues.push({
|
|
136
|
-
path: `nodes[${nodeId}].inputs.detail.savedFilterTrees.where`,
|
|
137
|
-
severity: "error",
|
|
138
|
-
message: "Connector node has `queryParameters.where` set but no " + "matching `savedFilterTrees.where` AST — neither at " + "`inputs.detail.savedFilterTrees.where` nor as " + "`essentialConfiguration.savedFilterTrees.where` inside " + "`inputs.detail.configuration`. CEQL filters must be authored " + "as both the raw filter expression (`queryParameters.where`) " + "AND a parallel canonical AST (`filters[].id` / `.operator` / " + "`.value.value`). The Studio Web canvas needs the AST to " + "render the filter UI; the runtime needs the expression. " + "Re-run `uip maestro flow node configure --detail.filter` to " + "author both. See uipath-maestro-flow skill — " + 'plugins/connector/impl.md, "Hand-authored CEQL strings" ' + "(MST-9265)."
|
|
139
|
-
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
function hasConnectorBinding(node) {
|
|
227
|
+
const detail = node.inputs?.detail;
|
|
228
|
+
if (!detail || typeof detail !== "object" || Array.isArray(detail)) {
|
|
229
|
+
return false;
|
|
140
230
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
231
|
+
const d = detail;
|
|
232
|
+
const authentication = d.authentication;
|
|
233
|
+
if (authentication === "connector") {
|
|
234
|
+
const connectionId = d.connectionId;
|
|
235
|
+
const targetConnector = d.targetConnector;
|
|
236
|
+
return typeof connectionId === "string" && connectionId.length > 0 && connectionId !== "ImplicitConnection" && typeof targetConnector === "string" && targetConnector.length > 0;
|
|
147
237
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const configuration = detail.configuration;
|
|
152
|
-
if (typeof configuration !== "string")
|
|
153
|
-
return;
|
|
154
|
-
const [, parsed] = catchError(() => JSON.parse(configuration.replace(/^=jsonString:/, "")));
|
|
155
|
-
if (!parsed)
|
|
156
|
-
return;
|
|
157
|
-
const essentialConfiguration = readObjectField(parsed, "essentialConfiguration");
|
|
158
|
-
if (!essentialConfiguration)
|
|
159
|
-
return;
|
|
160
|
-
return readObjectField(essentialConfiguration, "savedFilterTrees");
|
|
161
|
-
}
|
|
162
|
-
function readObjectField(parent, key) {
|
|
163
|
-
const value = parent[key];
|
|
164
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
165
|
-
return;
|
|
238
|
+
if (authentication === "manual") {
|
|
239
|
+
const url = d.url;
|
|
240
|
+
return typeof url === "string" && url.length > 0;
|
|
166
241
|
}
|
|
167
|
-
return
|
|
168
|
-
}
|
|
169
|
-
function isNonEmptyString(value) {
|
|
170
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
171
|
-
}
|
|
172
|
-
function isNonEmptyObject(value) {
|
|
173
|
-
return !!value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0;
|
|
242
|
+
return false;
|
|
174
243
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
244
|
+
function classifyLabel(label, catalog) {
|
|
245
|
+
const tokens = tokenize(label);
|
|
246
|
+
if (tokens.size === 0)
|
|
247
|
+
return null;
|
|
248
|
+
for (const bag of catalog.activityTokenBags) {
|
|
249
|
+
let overlap = 0;
|
|
250
|
+
for (const t of bag.tokens) {
|
|
251
|
+
if (tokens.has(t))
|
|
252
|
+
overlap += 1;
|
|
253
|
+
}
|
|
254
|
+
if (overlap >= 2) {
|
|
255
|
+
const keyword = pickConnectorKeyword(tokens, catalog.keywords);
|
|
256
|
+
return {
|
|
257
|
+
connectorKeyword: keyword ?? bag.connectorKey,
|
|
258
|
+
matchedActivity: {
|
|
259
|
+
connectorKey: bag.connectorKey,
|
|
260
|
+
connectorName: bag.connectorName
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const matchedKeyword = pickConnectorKeyword(tokens, catalog.keywords);
|
|
266
|
+
if (!matchedKeyword)
|
|
267
|
+
return null;
|
|
268
|
+
const hasVerb = [...tokens].some((t) => ACTION_VERBS.includes(t));
|
|
269
|
+
if (!hasVerb)
|
|
270
|
+
return null;
|
|
271
|
+
return { connectorKeyword: matchedKeyword };
|
|
272
|
+
}
|
|
273
|
+
function pickConnectorKeyword(tokens, keywords) {
|
|
274
|
+
for (const t of tokens) {
|
|
275
|
+
if (keywords.has(t))
|
|
276
|
+
return t;
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
function buildDiagnosticMessage(label, classification, strict) {
|
|
281
|
+
const connectorHint = classification.matchedActivity ? `looks like a "${classification.matchedActivity.connectorName}" connector activity` : `mentions the "${classification.connectorKeyword}" connector keyword`;
|
|
282
|
+
const strictNote = strict ? " (--strict-bindings promoted this to an error.)" : " Pass `--strict-bindings` to fail validation on this.";
|
|
283
|
+
const connectorDetail = `{"authentication":"connector",` + `"targetConnector":"${classification.connectorKeyword}",` + `"connectionId":"<uuid>","folderKey":"<uuid>","method":"GET","url":"<path>"}`;
|
|
284
|
+
const manualDetail = `{"authentication":"manual","method":"GET","url":"https://…"}`;
|
|
285
|
+
return `Node "${label}" ${connectorHint} but uses the generic Managed HTTP type \`${MANAGED_HTTP_NODE_TYPE}\` ` + "with no connection binding. The runtime will issue a raw HTTP call to an undefined endpoint. " + `Either bind a real Integration Service connection with \`uip maestro flow node configure <file> <node-id> --detail '${connectorDetail}'\`, ` + `or configure manual-mode HTTP with \`--detail '${manualDetail}'\`. ` + "If this node is intentionally a raw HTTP call, rename it so the label doesn't " + `imply a connector binding.${strictNote}`;
|
|
286
|
+
}
|
|
287
|
+
function validateConnectorBindings(workflow, catalog, options = {}) {
|
|
288
|
+
const issues = [];
|
|
289
|
+
const severity = options.strict ? "error" : "warning";
|
|
290
|
+
const checkNode = (node, scope) => {
|
|
291
|
+
if (node.type !== MANAGED_HTTP_NODE_TYPE)
|
|
292
|
+
return;
|
|
293
|
+
if (hasConnectorBinding(node))
|
|
294
|
+
return;
|
|
295
|
+
const label = readNodeLabel(node);
|
|
296
|
+
if (!label)
|
|
297
|
+
return;
|
|
298
|
+
const classification = classifyLabel(label, catalog);
|
|
299
|
+
if (!classification)
|
|
300
|
+
return;
|
|
301
|
+
issues.push({
|
|
302
|
+
path: `${scope}nodes[${node.id}].inputs.detail`,
|
|
303
|
+
severity,
|
|
304
|
+
message: buildDiagnosticMessage(label, classification, options.strict === true)
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
for (const node of workflow.nodes) {
|
|
308
|
+
checkNode(node, "");
|
|
309
|
+
}
|
|
310
|
+
for (const [subflowId, sub] of Object.entries(workflow.subflows ?? {})) {
|
|
311
|
+
for (const node of sub.nodes ?? []) {
|
|
312
|
+
checkNode(node, `subflows[${subflowId}].`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return issues;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/services/expression-validator.ts
|
|
319
|
+
var catalogRegistered = false;
|
|
320
|
+
async function ensureCatalogRegistered() {
|
|
321
|
+
if (catalogRegistered || !instance.isInitialized)
|
|
322
|
+
return;
|
|
323
|
+
try {
|
|
324
|
+
const { registerFlowExpressionModelI18n } = await import("./localization-h8a53z6p.js");
|
|
325
|
+
registerFlowExpressionModelI18n(instance);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
logger.warn(`Expression type checking localization unavailable, falling back to English: ${extractErrorMessageSync(error)}`);
|
|
328
|
+
}
|
|
329
|
+
catalogRegistered = true;
|
|
330
|
+
}
|
|
331
|
+
var headlessModule;
|
|
332
|
+
function loadHeadless() {
|
|
333
|
+
headlessModule ??= import("./headless-h1myzz32.js").then((mod) => {
|
|
334
|
+
logger.info(`Expression type checking: @uipath/flow-expression-model (TypeScript ${mod.HEADLESS_TYPESCRIPT_VERSION})`);
|
|
335
|
+
return mod;
|
|
336
|
+
}).catch((error) => {
|
|
337
|
+
logger.warn(`Expression type checking unavailable, skipping: ${extractErrorMessageSync(error)}`);
|
|
338
|
+
return null;
|
|
339
|
+
});
|
|
340
|
+
return headlessModule;
|
|
341
|
+
}
|
|
342
|
+
function findingPath(finding) {
|
|
343
|
+
if (!finding.nodeId)
|
|
344
|
+
return finding.fieldPath ?? "(workflow)";
|
|
345
|
+
if (!finding.fieldPath)
|
|
346
|
+
return `nodes[${finding.nodeId}]`;
|
|
347
|
+
return `nodes[${finding.nodeId}].${finding.fieldPath}`;
|
|
348
|
+
}
|
|
349
|
+
async function validateExpressions(graph, pathPrefix, strict = false) {
|
|
350
|
+
await ensureCatalogRegistered();
|
|
351
|
+
const headless = await loadHeadless();
|
|
352
|
+
if (!headless)
|
|
353
|
+
return [];
|
|
354
|
+
const [error, findings] = await catchError(headless.checkWorkflowExpressionsHeadless(graph));
|
|
355
|
+
if (error || !findings) {
|
|
356
|
+
logger.warn(`Expression type checking failed, skipping: ${extractErrorMessageSync(error)}`);
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
return findings.map((finding) => ({
|
|
360
|
+
path: `${pathPrefix}${findingPath(finding)}`,
|
|
361
|
+
message: `[${finding.code}] ${finding.message}`,
|
|
362
|
+
severity: strict ? "error" : "warning"
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/services/node-validators/activity-type-id-validator.ts
|
|
367
|
+
var activityTypeIdValidator = {
|
|
368
|
+
id: "activity-type-id",
|
|
369
|
+
appliesTo: (node) => {
|
|
370
|
+
const t = node.type;
|
|
371
|
+
if (!t)
|
|
372
|
+
return false;
|
|
373
|
+
return t === "core.action.http.v2" || t.startsWith("uipath.connector.");
|
|
374
|
+
},
|
|
185
375
|
validate: (node) => {
|
|
186
376
|
const rawDetail = node.inputs?.detail;
|
|
187
377
|
if (!rawDetail || typeof rawDetail !== "object" || Array.isArray(rawDetail)) {
|
|
@@ -5827,97 +6017,153 @@ var ceqlQueryValidator = {
|
|
|
5827
6017
|
}
|
|
5828
6018
|
};
|
|
5829
6019
|
|
|
5830
|
-
// src/services/node-validators/
|
|
5831
|
-
var
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
if (!
|
|
5842
|
-
return
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
6020
|
+
// src/services/node-validators/connector-validator.ts
|
|
6021
|
+
var connectorNodeValidator = {
|
|
6022
|
+
id: "uipath-connector",
|
|
6023
|
+
appliesTo: (node) => node.type?.startsWith("uipath.connector") ?? false,
|
|
6024
|
+
validate: (node) => {
|
|
6025
|
+
const rawDetail = node.inputs?.detail;
|
|
6026
|
+
const emptyDetailWarning = {
|
|
6027
|
+
path: `nodes[${node.id}].inputs.detail`,
|
|
6028
|
+
severity: "warning",
|
|
6029
|
+
message: "Connector is not configured, no detail found. " + "Run `uip maestro flow node configure <file> <node-id> " + `--detail '{"connectionId":"<id>","folderKey":"<key>",...}'\` ` + "to bind an Integration Service connection."
|
|
6030
|
+
};
|
|
6031
|
+
if (!rawDetail || typeof rawDetail !== "object" || Array.isArray(rawDetail)) {
|
|
6032
|
+
return [emptyDetailWarning];
|
|
6033
|
+
}
|
|
6034
|
+
const detail = rawDetail;
|
|
6035
|
+
if (Object.keys(detail).length === 0)
|
|
6036
|
+
return [emptyDetailWarning];
|
|
6037
|
+
const isEventDriven = isEventDrivenConnectorType(node.type);
|
|
6038
|
+
const context = {
|
|
6039
|
+
nodeId: node.id,
|
|
6040
|
+
nodeKind: isEventDriven ? "trigger" : "activity",
|
|
6041
|
+
connectionId: detail.connectionId,
|
|
6042
|
+
folderKey: detail.connectionFolderKey,
|
|
6043
|
+
objectName: detail.objectName ?? detail.connector,
|
|
6044
|
+
httpMethod: detail.method,
|
|
6045
|
+
endpoint: detail.endpoint,
|
|
6046
|
+
eventMode: detail.eventMode,
|
|
6047
|
+
eventType: detail.eventType,
|
|
6048
|
+
bodyParameters: detail.bodyParameters,
|
|
6049
|
+
queryParameters: detail.queryParameters,
|
|
6050
|
+
pathParameters: detail.pathParameters,
|
|
6051
|
+
eventParameters: detail.eventParameters,
|
|
6052
|
+
configuration: detail.configuration
|
|
6053
|
+
};
|
|
6054
|
+
const issues = validateIntSvcNode(context).map((issue) => ({
|
|
6055
|
+
path: `nodes[${node.id}].inputs.detail`,
|
|
6056
|
+
message: issue.title,
|
|
6057
|
+
severity: issue.type === "Error" ? "error" : "warning"
|
|
6058
|
+
}));
|
|
6059
|
+
issues.push(...validateCeqlFilterShape(node.id, detail));
|
|
6060
|
+
if (!isEventDriven) {
|
|
6061
|
+
issues.push(...validateEndpointMatchesActivityPath(node.id, detail));
|
|
6062
|
+
}
|
|
6063
|
+
return issues;
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
function staticSegments(path, substitutedValues) {
|
|
6067
|
+
const cut = path.search(/[?#]/);
|
|
6068
|
+
return (cut === -1 ? path : path.slice(0, cut)).split("/").filter((seg) => seg.length > 0).filter((seg) => !(seg.startsWith("{") && seg.endsWith("}"))).filter((seg) => !substitutedValues.has(seg));
|
|
5850
6069
|
}
|
|
5851
|
-
function
|
|
5852
|
-
|
|
6070
|
+
function endpointNamesActivity(endpointSegments, activitySegments) {
|
|
6071
|
+
const extra = endpointSegments.length - activitySegments.length;
|
|
6072
|
+
if (extra < 0 || extra > 1)
|
|
6073
|
+
return false;
|
|
6074
|
+
return activitySegments.every((seg, i2) => seg === endpointSegments[i2]);
|
|
5853
6075
|
}
|
|
5854
|
-
function
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
issues.push({
|
|
5858
|
-
path,
|
|
5859
|
-
severity,
|
|
5860
|
-
message: crossNodeBindingMessage(obj, ref2)
|
|
5861
|
-
});
|
|
5862
|
-
}
|
|
6076
|
+
function readEssentialConfiguration(detail) {
|
|
6077
|
+
const configuration = detail.configuration;
|
|
6078
|
+
if (typeof configuration !== "string")
|
|
5863
6079
|
return;
|
|
5864
|
-
|
|
5865
|
-
if (Array.isArray(
|
|
5866
|
-
for (let i2 = 0;i2 < obj.length; i2++) {
|
|
5867
|
-
scanContainerForCrossNodeBindings(obj[i2], `${path}[${i2}]`, nodeIds, severity, issues);
|
|
5868
|
-
}
|
|
6080
|
+
const [, parsed] = catchError(() => JSON.parse(configuration.replace(/^=jsonString:/, "")));
|
|
6081
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5869
6082
|
return;
|
|
5870
6083
|
}
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
6084
|
+
return readObjectField(parsed, "essentialConfiguration");
|
|
6085
|
+
}
|
|
6086
|
+
function readEssentialPath(detail) {
|
|
6087
|
+
const path = readEssentialConfiguration(detail)?.path;
|
|
6088
|
+
return typeof path === "string" && path.length > 0 ? path : undefined;
|
|
6089
|
+
}
|
|
6090
|
+
function validateEndpointMatchesActivityPath(nodeId, detail) {
|
|
6091
|
+
const endpoint = detail.endpoint;
|
|
6092
|
+
if (typeof endpoint !== "string" || endpoint.length === 0)
|
|
6093
|
+
return [];
|
|
6094
|
+
const activityPath = readEssentialPath(detail);
|
|
6095
|
+
if (!activityPath)
|
|
6096
|
+
return [];
|
|
6097
|
+
const substituted = new Set;
|
|
6098
|
+
const pathParameters = detail.pathParameters;
|
|
6099
|
+
if (pathParameters && typeof pathParameters === "object") {
|
|
6100
|
+
for (const v of Object.values(pathParameters)) {
|
|
6101
|
+
if (typeof v === "string" || typeof v === "number") {
|
|
6102
|
+
substituted.add(String(v));
|
|
6103
|
+
}
|
|
5874
6104
|
}
|
|
5875
6105
|
}
|
|
5876
|
-
|
|
5877
|
-
var PARAMETER_CONTAINER_KEYS = [
|
|
5878
|
-
"bodyParameters",
|
|
5879
|
-
"queryParameters",
|
|
5880
|
-
"pathParameters"
|
|
5881
|
-
];
|
|
5882
|
-
function scanDetailForCrossNodeBindings(detail, nodeIds, severity = "warning") {
|
|
5883
|
-
if (!detail || nodeIds.size === 0)
|
|
6106
|
+
if (endpointNamesActivity(staticSegments(endpoint, substituted), staticSegments(activityPath, substituted))) {
|
|
5884
6107
|
return [];
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
6108
|
+
}
|
|
6109
|
+
return [
|
|
6110
|
+
{
|
|
6111
|
+
path: `nodes[${nodeId}].inputs.detail.endpoint`,
|
|
6112
|
+
severity: "warning",
|
|
6113
|
+
message: `endpoint "${endpoint}" does not name the activity this node is configured for — its path is "${activityPath}" ` + "(essentialConfiguration.path). The runtime calls the endpoint verbatim, so the request reaches a different operation and fails at debug " + "with the provider's words for a missing or unknown field (e.g. Slack [102003] no_text when the v1 endpoint receives the v2 body). " + `Re-run node configure with --detail.endpoint "${activityPath}" (keep {param} segments; values go in pathParameters).`
|
|
5890
6114
|
}
|
|
5891
|
-
|
|
6115
|
+
];
|
|
6116
|
+
}
|
|
6117
|
+
function validateCeqlFilterShape(nodeId, detail) {
|
|
6118
|
+
const issues = [];
|
|
6119
|
+
const queryParameters = readObjectField(detail, "queryParameters");
|
|
6120
|
+
if (queryParameters && Object.hasOwn(queryParameters, "$where")) {
|
|
6121
|
+
issues.push({
|
|
6122
|
+
path: `nodes[${nodeId}].inputs.detail.queryParameters["$where"]`,
|
|
6123
|
+
severity: "error",
|
|
6124
|
+
message: 'Connector node uses OData-style `"$where"` (with leading `$`) ' + "in queryParameters. CEQL uses bare `where` — rename the key " + 'from `"$where"` to `"where"`. The leading `$` is the OData ' + "system-query-option prefix and is not recognized by " + "uipath.connector.* nodes. See uipath-maestro-flow skill — " + 'plugins/connector/impl.md, "Hand-authored CEQL strings" ' + "(MST-9265)."
|
|
6125
|
+
});
|
|
6126
|
+
}
|
|
6127
|
+
const queryWhere = queryParameters?.where;
|
|
6128
|
+
const queryWhereAuthored = isNonEmptyString(queryWhere);
|
|
6129
|
+
const queryWhereIsWholeValueExpression = typeof queryWhere === "string" && queryWhere.startsWith("=js:");
|
|
6130
|
+
const savedTrees = readObjectField(detail, "savedFilterTrees") ?? readEssentialSavedFilterTrees(detail);
|
|
6131
|
+
const savedWhereAuthored = savedTrees !== undefined && isNonEmptyObject(savedTrees.where);
|
|
6132
|
+
if (queryWhereAuthored && !queryWhereIsWholeValueExpression && !savedWhereAuthored) {
|
|
6133
|
+
issues.push({
|
|
6134
|
+
path: `nodes[${nodeId}].inputs.detail.savedFilterTrees.where`,
|
|
6135
|
+
severity: "error",
|
|
6136
|
+
message: "Connector node has `queryParameters.where` set but no " + "matching `savedFilterTrees.where` AST — neither at " + "`inputs.detail.savedFilterTrees.where` nor as " + "`essentialConfiguration.savedFilterTrees.where` inside " + "`inputs.detail.configuration`. CEQL filters must be authored " + "as both the raw filter expression (`queryParameters.where`) " + "AND a parallel canonical AST (`filters[].id` / `.operator` / " + "`.value.value`). The Studio Web canvas needs the AST to " + "render the filter UI; the runtime needs the expression. " + "Re-run `uip maestro flow node configure --detail.filter` to " + "author both. See uipath-maestro-flow skill — " + 'plugins/connector/impl.md, "Hand-authored CEQL strings" ' + "(MST-9265)."
|
|
6137
|
+
});
|
|
6138
|
+
}
|
|
6139
|
+
if (savedWhereAuthored && !queryWhereAuthored) {
|
|
6140
|
+
issues.push({
|
|
6141
|
+
path: `nodes[${nodeId}].inputs.detail.queryParameters.where`,
|
|
6142
|
+
severity: "error",
|
|
6143
|
+
message: "Connector node has `savedFilterTrees.where` AST but no " + "matching `queryParameters.where` expression. CEQL filters " + "must be authored on both keys in parallel — the runtime " + "evaluates `queryParameters.where`, not the AST. See " + 'uipath-maestro-flow skill — plugins/connector/impl.md, "Hand-authored ' + 'CEQL strings" (MST-9265).'
|
|
6144
|
+
});
|
|
5892
6145
|
}
|
|
5893
6146
|
return issues;
|
|
5894
6147
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
validate: (node, context) => {
|
|
5906
|
-
const nodeIds = context.nodeIds ?? new Set;
|
|
5907
|
-
if (nodeIds.size === 0)
|
|
5908
|
-
return [];
|
|
5909
|
-
const rawDetail = node.inputs?.detail;
|
|
5910
|
-
if (!rawDetail || typeof rawDetail !== "object" || Array.isArray(rawDetail)) {
|
|
5911
|
-
return [];
|
|
5912
|
-
}
|
|
5913
|
-
const severity = context.strictBindings ? "error" : "warning";
|
|
5914
|
-
const issues = scanDetailForCrossNodeBindings(rawDetail, nodeIds, severity);
|
|
5915
|
-
return issues.map((issue) => ({
|
|
5916
|
-
...issue,
|
|
5917
|
-
path: `nodes[${node.id}].inputs.${issue.path}`
|
|
5918
|
-
}));
|
|
6148
|
+
function readEssentialSavedFilterTrees(detail) {
|
|
6149
|
+
const essentialConfiguration = readEssentialConfiguration(detail);
|
|
6150
|
+
if (!essentialConfiguration)
|
|
6151
|
+
return;
|
|
6152
|
+
return readObjectField(essentialConfiguration, "savedFilterTrees");
|
|
6153
|
+
}
|
|
6154
|
+
function readObjectField(parent, key) {
|
|
6155
|
+
const value = parent[key];
|
|
6156
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6157
|
+
return;
|
|
5919
6158
|
}
|
|
5920
|
-
|
|
6159
|
+
return value;
|
|
6160
|
+
}
|
|
6161
|
+
function isNonEmptyString(value) {
|
|
6162
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
6163
|
+
}
|
|
6164
|
+
function isNonEmptyObject(value) {
|
|
6165
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0;
|
|
6166
|
+
}
|
|
5921
6167
|
|
|
5922
6168
|
// src/services/node-validators/expression-prefix-validator.ts
|
|
5923
6169
|
var expressionPrefixValidator = {
|
|
@@ -6098,7 +6344,8 @@ var jsExpressionSyntaxValidator = {
|
|
|
6098
6344
|
for (const containerKey of [
|
|
6099
6345
|
"bodyParameters",
|
|
6100
6346
|
"queryParameters",
|
|
6101
|
-
"pathParameters"
|
|
6347
|
+
"pathParameters",
|
|
6348
|
+
"filterVariables"
|
|
6102
6349
|
]) {
|
|
6103
6350
|
const container = detail[containerKey];
|
|
6104
6351
|
if (!container || typeof container !== "object" || Array.isArray(container)) {
|
|
@@ -6147,1632 +6394,561 @@ function scanForInvalidJsExpressions(obj, path, severity, issues) {
|
|
|
6147
6394
|
}
|
|
6148
6395
|
return;
|
|
6149
6396
|
}
|
|
6150
|
-
if (obj && typeof obj === "object") {
|
|
6151
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
6152
|
-
scanForInvalidJsExpressions(value, `${path}.${key}`, severity, issues);
|
|
6153
|
-
}
|
|
6154
|
-
}
|
|
6155
|
-
}
|
|
6156
|
-
function reportInvalidExpression(expression, path, severity, issues) {
|
|
6157
|
-
if (expression.trim() === "")
|
|
6158
|
-
return;
|
|
6159
|
-
const error = syntaxErrorFrom(() => parse3(`(${expression}
|
|
6160
|
-
)`, {
|
|
6161
|
-
ecmaVersion: 2020,
|
|
6162
|
-
sourceType: "script"
|
|
6163
|
-
}));
|
|
6164
|
-
if (!error)
|
|
6165
|
-
return;
|
|
6166
|
-
issues.push({
|
|
6167
|
-
path,
|
|
6168
|
-
severity,
|
|
6169
|
-
message: `Expression at this field is not valid JavaScript: ${error.message}. Fix the syntax — validate cannot otherwise catch JavaScript expression errors; they surface only at flow debug.`
|
|
6170
|
-
});
|
|
6171
|
-
}
|
|
6172
|
-
function syntaxErrorFrom(parseSource) {
|
|
6173
|
-
try {
|
|
6174
|
-
parseSource();
|
|
6175
|
-
return;
|
|
6176
|
-
} catch (err) {
|
|
6177
|
-
return err instanceof Error ? err : new Error(String(err));
|
|
6178
|
-
}
|
|
6179
|
-
}
|
|
6180
|
-
|
|
6181
|
-
// src/services/node-validators/model-source-validator.ts
|
|
6182
|
-
var modelSourceValidator = {
|
|
6183
|
-
id: "model-source",
|
|
6184
|
-
appliesTo: () => true,
|
|
6185
|
-
validate: (node, context) => {
|
|
6186
|
-
const definition = context.definitions.find((d) => d.nodeType === node.type);
|
|
6187
|
-
if (!definition)
|
|
6188
|
-
return [];
|
|
6189
|
-
const requiresModelSource = readModelSourceFlag(definition);
|
|
6190
|
-
if (!requiresModelSource)
|
|
6191
|
-
return [];
|
|
6192
|
-
const legacySource = readLegacyModelSource(node);
|
|
6193
|
-
if (legacySource !== undefined) {
|
|
6194
|
-
return [
|
|
6195
|
-
{
|
|
6196
|
-
path: `nodes[${node.id}].model.source`,
|
|
6197
|
-
severity: "error",
|
|
6198
|
-
message: `Node type "${node.type}" carries the deprecated ` + `"model.source" UUID on the node instance. ` + `flow-core 0.2.50+ requires the canvas-generated ` + `UUID at "inputs.source"; remove "model.source" ` + `from the node and place the value under ` + `"inputs": { "source": "<uuid>" } instead. ` + `See uipath-maestro-flow skill — ` + `inline-agent/impl.md (MST-9265).`
|
|
6199
|
-
}
|
|
6200
|
-
];
|
|
6201
|
-
}
|
|
6202
|
-
const inputsSource = node.inputs?.source;
|
|
6203
|
-
if (typeof inputsSource === "string" && inputsSource.length > 0) {
|
|
6204
|
-
return [];
|
|
6205
|
-
}
|
|
6206
|
-
return [
|
|
6207
|
-
{
|
|
6208
|
-
path: `nodes[${node.id}].inputs.source`,
|
|
6209
|
-
severity: "error",
|
|
6210
|
-
message: `Node type "${node.type}" requires a stable UUID at ` + `"inputs.source" — the agent's ProjectId for inline ` + `agent nodes, or the resource id from the parent inline ` + `agent's "resources[]" for agent-tool resource nodes. ` + `Set "inputs": { "source": "<uuid>" } on the node ` + `(use the ProjectId returned by ` + `"uip agent init --inline-in-flow" for inline agents). ` + `See uipath-maestro-flow skill — inline-agent/impl.md ` + `(MST-9265).`
|
|
6211
|
-
}
|
|
6212
|
-
];
|
|
6213
|
-
}
|
|
6214
|
-
};
|
|
6215
|
-
function readModelSourceFlag(definition) {
|
|
6216
|
-
const model = definition.model;
|
|
6217
|
-
if (!model || typeof model !== "object" || Array.isArray(model)) {
|
|
6218
|
-
return false;
|
|
6219
|
-
}
|
|
6220
|
-
const source = model.source;
|
|
6221
|
-
return source === true;
|
|
6222
|
-
}
|
|
6223
|
-
function readLegacyModelSource(node) {
|
|
6224
|
-
const model = node.model;
|
|
6225
|
-
if (!model || typeof model !== "object" || Array.isArray(model)) {
|
|
6226
|
-
return;
|
|
6227
|
-
}
|
|
6228
|
-
const source = model.source;
|
|
6229
|
-
if (typeof source === "string" && source.length > 0) {
|
|
6230
|
-
return source;
|
|
6231
|
-
}
|
|
6232
|
-
return;
|
|
6233
|
-
}
|
|
6234
|
-
|
|
6235
|
-
// src/services/node-validators/index.ts
|
|
6236
|
-
var defaultNodeValidators = [
|
|
6237
|
-
activityTypeIdValidator,
|
|
6238
|
-
ceqlQueryValidator,
|
|
6239
|
-
connectorNodeValidator,
|
|
6240
|
-
crossNodeBindingValidator,
|
|
6241
|
-
expressionPrefixValidator,
|
|
6242
|
-
jsExpressionSyntaxValidator,
|
|
6243
|
-
ixpNodeValidator,
|
|
6244
|
-
modelSourceValidator
|
|
6245
|
-
];
|
|
6246
|
-
|
|
6247
|
-
// src/utils/error-edge-handling.ts
|
|
6248
|
-
var ERROR_SOURCE_PORT = "error";
|
|
6249
|
-
function collectErrorEdgeSourceNodeIds(edges) {
|
|
6250
|
-
return new Set(edges.filter((edge) => edge.sourcePort === ERROR_SOURCE_PORT).map((edge) => edge.sourceNodeId));
|
|
6251
|
-
}
|
|
6252
|
-
function enableErrorHandlingForErrorEdges(nodes, edges) {
|
|
6253
|
-
const sourceNodeIds = collectErrorEdgeSourceNodeIds(edges);
|
|
6254
|
-
let updated = 0;
|
|
6255
|
-
for (const node of nodes) {
|
|
6256
|
-
if (!sourceNodeIds.has(node.id))
|
|
6257
|
-
continue;
|
|
6258
|
-
if (node.inputs?.errorHandlingEnabled === true)
|
|
6259
|
-
continue;
|
|
6260
|
-
node.inputs = {
|
|
6261
|
-
...node.inputs ?? {},
|
|
6262
|
-
errorHandlingEnabled: true
|
|
6263
|
-
};
|
|
6264
|
-
updated += 1;
|
|
6265
|
-
}
|
|
6266
|
-
return updated;
|
|
6267
|
-
}
|
|
6268
|
-
|
|
6269
|
-
// src/utils/handle-expansion.ts
|
|
6270
|
-
var VISIBLE_INPUT_EXPRESSION = /^\{inputs\.([A-Za-z0-9_]+)\}$/;
|
|
6271
|
-
function parseVisibleInputKey(visible) {
|
|
6272
|
-
if (typeof visible !== "string")
|
|
6273
|
-
return null;
|
|
6274
|
-
const match = VISIBLE_INPUT_EXPRESSION.exec(visible);
|
|
6275
|
-
return match ? match[1] : null;
|
|
6276
|
-
}
|
|
6277
|
-
function isHandleVisible(handle, node) {
|
|
6278
|
-
const { visible } = handle;
|
|
6279
|
-
if (visible === undefined || visible === true)
|
|
6280
|
-
return true;
|
|
6281
|
-
if (visible === false)
|
|
6282
|
-
return false;
|
|
6283
|
-
const inputKey = parseVisibleInputKey(visible);
|
|
6284
|
-
if (inputKey === null) {
|
|
6285
|
-
return true;
|
|
6286
|
-
}
|
|
6287
|
-
return node.inputs?.[inputKey] === true;
|
|
6288
|
-
}
|
|
6289
|
-
function expandHandleIds(handle, node) {
|
|
6290
|
-
if (!handle.repeat) {
|
|
6291
|
-
return [handle.id];
|
|
6292
|
-
}
|
|
6293
|
-
const repeatPath = handle.repeat.split(".");
|
|
6294
|
-
let collection = node;
|
|
6295
|
-
for (const segment of repeatPath) {
|
|
6296
|
-
if (collection && typeof collection === "object") {
|
|
6297
|
-
collection = collection[segment];
|
|
6298
|
-
} else {
|
|
6299
|
-
collection = undefined;
|
|
6300
|
-
break;
|
|
6301
|
-
}
|
|
6302
|
-
}
|
|
6303
|
-
if (!Array.isArray(collection)) {
|
|
6304
|
-
return [handle.id];
|
|
6305
|
-
}
|
|
6306
|
-
return collection.filter((item) => item != null && typeof item === "object").map((item) => handle.id.replace(/\{item\.(\w+)\}/g, (_match, key) => String(item[key] ?? "")));
|
|
6307
|
-
}
|
|
6308
|
-
function isTemplatedHandleId(id) {
|
|
6309
|
-
return id.includes("{");
|
|
6310
|
-
}
|
|
6311
|
-
var escapeRegexMetachars = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6312
|
-
function portMatchesTemplate(requestedPort, templateId) {
|
|
6313
|
-
if (!isTemplatedHandleId(templateId)) {
|
|
6314
|
-
return requestedPort === templateId;
|
|
6315
|
-
}
|
|
6316
|
-
const parts = templateId.split(/\{[^}]*\}/);
|
|
6317
|
-
const escapedParts = parts.map(escapeRegexMetachars);
|
|
6318
|
-
const pattern = new RegExp(`^${escapedParts.join(".+")}$`);
|
|
6319
|
-
return pattern.test(requestedPort);
|
|
6320
|
-
}
|
|
6321
|
-
|
|
6322
|
-
// src/services/connector-binding-validator.ts
|
|
6323
|
-
var STATIC_CONNECTOR_KEYWORDS = [
|
|
6324
|
-
"slack",
|
|
6325
|
-
"gmail",
|
|
6326
|
-
"outlook",
|
|
6327
|
-
"office365",
|
|
6328
|
-
"exchange",
|
|
6329
|
-
"jira",
|
|
6330
|
-
"confluence",
|
|
6331
|
-
"salesforce",
|
|
6332
|
-
"sfdc",
|
|
6333
|
-
"hubspot",
|
|
6334
|
-
"servicenow",
|
|
6335
|
-
"zendesk",
|
|
6336
|
-
"intercom",
|
|
6337
|
-
"notion",
|
|
6338
|
-
"asana",
|
|
6339
|
-
"trello",
|
|
6340
|
-
"monday",
|
|
6341
|
-
"github",
|
|
6342
|
-
"gitlab",
|
|
6343
|
-
"bitbucket",
|
|
6344
|
-
"discord",
|
|
6345
|
-
"teams",
|
|
6346
|
-
"sharepoint",
|
|
6347
|
-
"onedrive",
|
|
6348
|
-
"dropbox",
|
|
6349
|
-
"box",
|
|
6350
|
-
"googledrive",
|
|
6351
|
-
"docusign",
|
|
6352
|
-
"adobesign",
|
|
6353
|
-
"mailchimp",
|
|
6354
|
-
"sendgrid",
|
|
6355
|
-
"twilio",
|
|
6356
|
-
"stripe",
|
|
6357
|
-
"shopify",
|
|
6358
|
-
"quickbooks",
|
|
6359
|
-
"xero",
|
|
6360
|
-
"workday",
|
|
6361
|
-
"successfactors",
|
|
6362
|
-
"bamboohr",
|
|
6363
|
-
"sap",
|
|
6364
|
-
"netsuite",
|
|
6365
|
-
"oracle",
|
|
6366
|
-
"marketo",
|
|
6367
|
-
"pardot",
|
|
6368
|
-
"segment",
|
|
6369
|
-
"snowflake",
|
|
6370
|
-
"bigquery",
|
|
6371
|
-
"redshift",
|
|
6372
|
-
"databricks",
|
|
6373
|
-
"tableau",
|
|
6374
|
-
"powerbi"
|
|
6375
|
-
];
|
|
6376
|
-
var ACTION_VERBS = [
|
|
6377
|
-
"send",
|
|
6378
|
-
"post",
|
|
6379
|
-
"create",
|
|
6380
|
-
"update",
|
|
6381
|
-
"delete",
|
|
6382
|
-
"get",
|
|
6383
|
-
"fetch",
|
|
6384
|
-
"list",
|
|
6385
|
-
"read",
|
|
6386
|
-
"write",
|
|
6387
|
-
"upload",
|
|
6388
|
-
"download",
|
|
6389
|
-
"find",
|
|
6390
|
-
"add",
|
|
6391
|
-
"remove",
|
|
6392
|
-
"query",
|
|
6393
|
-
"search",
|
|
6394
|
-
"notify",
|
|
6395
|
-
"publish",
|
|
6396
|
-
"subscribe",
|
|
6397
|
-
"trigger",
|
|
6398
|
-
"watch",
|
|
6399
|
-
"share",
|
|
6400
|
-
"invite",
|
|
6401
|
-
"schedule",
|
|
6402
|
-
"assign",
|
|
6403
|
-
"close",
|
|
6404
|
-
"open",
|
|
6405
|
-
"reply",
|
|
6406
|
-
"forward",
|
|
6407
|
-
"archive"
|
|
6408
|
-
];
|
|
6409
|
-
var MANAGED_HTTP_NODE_TYPE = "core.action.http.v2";
|
|
6410
|
-
async function loadConnectorCatalog() {
|
|
6411
|
-
const keywords2 = new Set(STATIC_CONNECTOR_KEYWORDS);
|
|
6412
|
-
const activityTokenBags = [];
|
|
6413
|
-
const [err, registry] = await catchError(loadRegistry());
|
|
6414
|
-
if (err) {
|
|
6415
|
-
logger.debug(`Connector-binding validator: could not load IS catalog cache (${err.message}); falling back to static keyword list.`);
|
|
6416
|
-
return { keywords: keywords2, activityTokenBags };
|
|
6417
|
-
}
|
|
6418
|
-
if (!registry) {
|
|
6419
|
-
return { keywords: keywords2, activityTokenBags };
|
|
6420
|
-
}
|
|
6421
|
-
for (const connector of registry.discovered.connectors) {
|
|
6422
|
-
addConnectorKeywords(connector, keywords2);
|
|
6423
|
-
for (const activity of connector.activities) {
|
|
6424
|
-
const tokens = tokenize(activity.displayName);
|
|
6425
|
-
if (tokens.size > 0) {
|
|
6426
|
-
activityTokenBags.push({
|
|
6427
|
-
connectorKey: connector.key,
|
|
6428
|
-
connectorName: connector.name,
|
|
6429
|
-
tokens
|
|
6430
|
-
});
|
|
6431
|
-
}
|
|
6432
|
-
}
|
|
6433
|
-
}
|
|
6434
|
-
return { keywords: keywords2, activityTokenBags };
|
|
6435
|
-
}
|
|
6436
|
-
function addConnectorKeywords(connector, out) {
|
|
6437
|
-
const keyParts = connector.key.toLowerCase().split("-").filter((p) => p && p !== "uipath");
|
|
6438
|
-
for (const part of keyParts)
|
|
6439
|
-
out.add(part);
|
|
6440
|
-
for (const t of tokenize(connector.name))
|
|
6441
|
-
out.add(t);
|
|
6442
|
-
}
|
|
6443
|
-
function tokenize(label) {
|
|
6444
|
-
if (!label)
|
|
6445
|
-
return new Set;
|
|
6446
|
-
const tokens = label.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1);
|
|
6447
|
-
return new Set(tokens);
|
|
6448
|
-
}
|
|
6449
|
-
function readNodeLabel(node) {
|
|
6450
|
-
const fromDisplay = node.display?.label;
|
|
6451
|
-
if (typeof fromDisplay === "string" && fromDisplay.length > 0) {
|
|
6452
|
-
return fromDisplay;
|
|
6453
|
-
}
|
|
6454
|
-
const fromName = node.name;
|
|
6455
|
-
if (typeof fromName === "string" && fromName.length > 0) {
|
|
6456
|
-
return fromName;
|
|
6457
|
-
}
|
|
6458
|
-
return;
|
|
6459
|
-
}
|
|
6460
|
-
function hasConnectorBinding(node) {
|
|
6461
|
-
const detail = node.inputs?.detail;
|
|
6462
|
-
if (!detail || typeof detail !== "object" || Array.isArray(detail)) {
|
|
6463
|
-
return false;
|
|
6464
|
-
}
|
|
6465
|
-
const d = detail;
|
|
6466
|
-
const authentication = d.authentication;
|
|
6467
|
-
if (authentication === "connector") {
|
|
6468
|
-
const connectionId = d.connectionId;
|
|
6469
|
-
const targetConnector = d.targetConnector;
|
|
6470
|
-
return typeof connectionId === "string" && connectionId.length > 0 && connectionId !== "ImplicitConnection" && typeof targetConnector === "string" && targetConnector.length > 0;
|
|
6471
|
-
}
|
|
6472
|
-
if (authentication === "manual") {
|
|
6473
|
-
const url = d.url;
|
|
6474
|
-
return typeof url === "string" && url.length > 0;
|
|
6475
|
-
}
|
|
6476
|
-
return false;
|
|
6477
|
-
}
|
|
6478
|
-
function classifyLabel(label, catalog) {
|
|
6479
|
-
const tokens = tokenize(label);
|
|
6480
|
-
if (tokens.size === 0)
|
|
6481
|
-
return null;
|
|
6482
|
-
for (const bag of catalog.activityTokenBags) {
|
|
6483
|
-
let overlap = 0;
|
|
6484
|
-
for (const t of bag.tokens) {
|
|
6485
|
-
if (tokens.has(t))
|
|
6486
|
-
overlap += 1;
|
|
6487
|
-
}
|
|
6488
|
-
if (overlap >= 2) {
|
|
6489
|
-
const keyword = pickConnectorKeyword(tokens, catalog.keywords);
|
|
6490
|
-
return {
|
|
6491
|
-
connectorKeyword: keyword ?? bag.connectorKey,
|
|
6492
|
-
matchedActivity: {
|
|
6493
|
-
connectorKey: bag.connectorKey,
|
|
6494
|
-
connectorName: bag.connectorName
|
|
6495
|
-
}
|
|
6496
|
-
};
|
|
6497
|
-
}
|
|
6498
|
-
}
|
|
6499
|
-
const matchedKeyword = pickConnectorKeyword(tokens, catalog.keywords);
|
|
6500
|
-
if (!matchedKeyword)
|
|
6501
|
-
return null;
|
|
6502
|
-
const hasVerb = [...tokens].some((t) => ACTION_VERBS.includes(t));
|
|
6503
|
-
if (!hasVerb)
|
|
6504
|
-
return null;
|
|
6505
|
-
return { connectorKeyword: matchedKeyword };
|
|
6506
|
-
}
|
|
6507
|
-
function pickConnectorKeyword(tokens, keywords2) {
|
|
6508
|
-
for (const t of tokens) {
|
|
6509
|
-
if (keywords2.has(t))
|
|
6510
|
-
return t;
|
|
6511
|
-
}
|
|
6512
|
-
return;
|
|
6513
|
-
}
|
|
6514
|
-
function buildDiagnosticMessage(label, classification, strict) {
|
|
6515
|
-
const connectorHint = classification.matchedActivity ? `looks like a "${classification.matchedActivity.connectorName}" connector activity` : `mentions the "${classification.connectorKeyword}" connector keyword`;
|
|
6516
|
-
const strictNote = strict ? " (--strict-bindings promoted this to an error.)" : " Pass `--strict-bindings` to fail validation on this.";
|
|
6517
|
-
const connectorDetail = `{"authentication":"connector",` + `"targetConnector":"${classification.connectorKeyword}",` + `"connectionId":"<uuid>","folderKey":"<uuid>","method":"GET","url":"<path>"}`;
|
|
6518
|
-
const manualDetail = `{"authentication":"manual","method":"GET","url":"https://…"}`;
|
|
6519
|
-
return `Node "${label}" ${connectorHint} but uses the generic Managed HTTP type \`${MANAGED_HTTP_NODE_TYPE}\` ` + "with no connection binding. The runtime will issue a raw HTTP call to an undefined endpoint. " + `Either bind a real Integration Service connection with \`uip maestro flow node configure <file> <node-id> --detail '${connectorDetail}'\`, ` + `or configure manual-mode HTTP with \`--detail '${manualDetail}'\`. ` + "If this node is intentionally a raw HTTP call, rename it so the label doesn't " + `imply a connector binding.${strictNote}`;
|
|
6520
|
-
}
|
|
6521
|
-
function validateConnectorBindings(workflow, catalog, options = {}) {
|
|
6522
|
-
const issues = [];
|
|
6523
|
-
const severity = options.strict ? "error" : "warning";
|
|
6524
|
-
const checkNode = (node, scope) => {
|
|
6525
|
-
if (node.type !== MANAGED_HTTP_NODE_TYPE)
|
|
6526
|
-
return;
|
|
6527
|
-
if (hasConnectorBinding(node))
|
|
6528
|
-
return;
|
|
6529
|
-
const label = readNodeLabel(node);
|
|
6530
|
-
if (!label)
|
|
6531
|
-
return;
|
|
6532
|
-
const classification = classifyLabel(label, catalog);
|
|
6533
|
-
if (!classification)
|
|
6534
|
-
return;
|
|
6535
|
-
issues.push({
|
|
6536
|
-
path: `${scope}nodes[${node.id}].inputs.detail`,
|
|
6537
|
-
severity,
|
|
6538
|
-
message: buildDiagnosticMessage(label, classification, options.strict === true)
|
|
6539
|
-
});
|
|
6540
|
-
};
|
|
6541
|
-
for (const node of workflow.nodes) {
|
|
6542
|
-
checkNode(node, "");
|
|
6543
|
-
}
|
|
6544
|
-
for (const [subflowId, sub] of Object.entries(workflow.subflows ?? {})) {
|
|
6545
|
-
for (const node of sub.nodes ?? []) {
|
|
6546
|
-
checkNode(node, `subflows[${subflowId}].`);
|
|
6547
|
-
}
|
|
6548
|
-
}
|
|
6549
|
-
return issues;
|
|
6550
|
-
}
|
|
6551
|
-
|
|
6552
|
-
// src/services/flow-eval-schema/prompts.ts
|
|
6553
|
-
var DEFAULT_EVALUATOR_PROMPT = `As an expert evaluator, analyze the semantic similarity of these JSON contents to determine a score from 0-100. Focus on comparing the meaning and contextual equivalence of corresponding fields, accounting for alternative valid expressions, synonyms, and reasonable variations in language while maintaining high standards for accuracy and completeness. Provide your score with a justification, explaining briefly and concisely why you gave that score.
|
|
6554
|
-
----
|
|
6555
|
-
ExpectedOutput:
|
|
6556
|
-
{{ExpectedOutput}}
|
|
6557
|
-
----
|
|
6558
|
-
ActualOutput:
|
|
6559
|
-
{{ActualOutput}}
|
|
6560
|
-
`;
|
|
6561
|
-
var DEFAULT_TRAJECTORY_EVALUATOR_PROMPT = `As an expert evaluator, determine how well the agent did on a scale of 0-100. Focus on if the simulation was successful and if the agent behaved according to the expected output accounting for alternative valid expressions, and reasonable variations in language while maintaining high standards for accuracy and completeness. Provide your score with a justification, explaining briefly and concisely why you gave that score.
|
|
6562
|
-
----
|
|
6563
|
-
UserOrSyntheticInputGivenToAgent:
|
|
6564
|
-
{{UserOrSyntheticInput}}
|
|
6565
|
-
----
|
|
6566
|
-
SimulationInstructions:
|
|
6567
|
-
{{SimulationInstructions}}
|
|
6568
|
-
----
|
|
6569
|
-
ExpectedAgentBehavior:
|
|
6570
|
-
{{ExpectedAgentBehavior}}
|
|
6571
|
-
----
|
|
6572
|
-
AgentRunHistory:
|
|
6573
|
-
{{AgentRunHistory}}
|
|
6574
|
-
`;
|
|
6575
|
-
|
|
6576
|
-
// src/services/flow-eval-schema/evaluator-fields.ts
|
|
6577
|
-
var targetOutputKeyConfigField = {
|
|
6578
|
-
key: "targetOutputKey",
|
|
6579
|
-
label: "Target Output Key",
|
|
6580
|
-
type: "targetOutputKey",
|
|
6581
|
-
required: false,
|
|
6582
|
-
description: "The key in the output to evaluate"
|
|
6583
|
-
};
|
|
6584
|
-
var caseSensitiveConfigField = {
|
|
6585
|
-
key: "caseSensitive",
|
|
6586
|
-
label: "Case Sensitive",
|
|
6587
|
-
type: "boolean",
|
|
6588
|
-
required: false,
|
|
6589
|
-
default: false,
|
|
6590
|
-
description: "If true, the comparison is case-sensitive"
|
|
6591
|
-
};
|
|
6592
|
-
var negatedConfigField = {
|
|
6593
|
-
key: "negated",
|
|
6594
|
-
label: "Negated",
|
|
6595
|
-
type: "boolean",
|
|
6596
|
-
required: false,
|
|
6597
|
-
default: false,
|
|
6598
|
-
description: "If true, the evaluation passes when the match is not found"
|
|
6599
|
-
};
|
|
6600
|
-
var expectedOutputCriteriaField = {
|
|
6601
|
-
key: "expectedOutput",
|
|
6602
|
-
label: "Expected Output",
|
|
6603
|
-
type: "object",
|
|
6604
|
-
required: false,
|
|
6605
|
-
description: "The expected output value for this evaluator"
|
|
6606
|
-
};
|
|
6607
|
-
var llmBaseConfigFields = [
|
|
6608
|
-
{
|
|
6609
|
-
key: "model",
|
|
6610
|
-
label: "Model",
|
|
6611
|
-
type: "llmModel",
|
|
6612
|
-
required: true,
|
|
6613
|
-
description: "The LLM model to use for judging"
|
|
6614
|
-
},
|
|
6615
|
-
{
|
|
6616
|
-
key: "prompt",
|
|
6617
|
-
label: "Evaluation Prompt",
|
|
6618
|
-
type: "string",
|
|
6619
|
-
required: true,
|
|
6620
|
-
default: DEFAULT_EVALUATOR_PROMPT,
|
|
6621
|
-
description: "Custom prompt template for the LLM judge"
|
|
6622
|
-
},
|
|
6623
|
-
{
|
|
6624
|
-
key: "temperature",
|
|
6625
|
-
label: "Temperature",
|
|
6626
|
-
type: "number",
|
|
6627
|
-
required: true,
|
|
6628
|
-
default: 0,
|
|
6629
|
-
min: 0,
|
|
6630
|
-
max: 2,
|
|
6631
|
-
step: 0.1,
|
|
6632
|
-
description: "Temperature for LLM generation"
|
|
6633
|
-
},
|
|
6634
|
-
{
|
|
6635
|
-
key: "maxTokens",
|
|
6636
|
-
label: "Max Tokens",
|
|
6637
|
-
type: "number",
|
|
6638
|
-
required: true,
|
|
6639
|
-
default: 8192,
|
|
6640
|
-
min: 0,
|
|
6641
|
-
max: 16384,
|
|
6642
|
-
step: 256,
|
|
6643
|
-
description: "Maximum tokens for LLM response"
|
|
6644
|
-
}
|
|
6645
|
-
];
|
|
6646
|
-
var llmTrajectoryBaseConfigFields = llmBaseConfigFields.map((field) => field.key === "prompt" ? { ...field, default: DEFAULT_TRAJECTORY_EVALUATOR_PROMPT } : field);
|
|
6647
|
-
|
|
6648
|
-
// src/services/flow-eval-schema/deterministic-evaluators.ts
|
|
6649
|
-
var DETERMINISTIC_EVALUATOR_TYPES = {
|
|
6650
|
-
"uipath-contains": {
|
|
6651
|
-
name: "Contains Text",
|
|
6652
|
-
description: "Check if output contains specific text",
|
|
6653
|
-
category: "deterministic",
|
|
6654
|
-
shortDescription: "Passes when output contains given text",
|
|
6655
|
-
configFields: [
|
|
6656
|
-
targetOutputKeyConfigField,
|
|
6657
|
-
caseSensitiveConfigField,
|
|
6658
|
-
{
|
|
6659
|
-
...negatedConfigField,
|
|
6660
|
-
label: "Negated (should NOT contain)"
|
|
6661
|
-
}
|
|
6662
|
-
],
|
|
6663
|
-
criteriaFields: [
|
|
6664
|
-
{
|
|
6665
|
-
key: "searchText",
|
|
6666
|
-
label: "Search Text",
|
|
6667
|
-
type: "string",
|
|
6668
|
-
required: false,
|
|
6669
|
-
minLength: 1,
|
|
6670
|
-
description: "The text to search for in the output"
|
|
6671
|
-
}
|
|
6672
|
-
],
|
|
6673
|
-
examples: {
|
|
6674
|
-
criteria: { searchText: "loan approved" }
|
|
6675
|
-
}
|
|
6676
|
-
},
|
|
6677
|
-
"uipath-exact-match": {
|
|
6678
|
-
name: "Exact Match",
|
|
6679
|
-
description: "Check if output exactly matches expected value",
|
|
6680
|
-
category: "deterministic",
|
|
6681
|
-
shortDescription: "Passes when output is an exact match",
|
|
6682
|
-
configFields: [
|
|
6683
|
-
targetOutputKeyConfigField,
|
|
6684
|
-
caseSensitiveConfigField,
|
|
6685
|
-
negatedConfigField
|
|
6686
|
-
],
|
|
6687
|
-
criteriaFields: [expectedOutputCriteriaField],
|
|
6688
|
-
examples: {
|
|
6689
|
-
criteria: { expectedOutput: { value: "Application approved" } }
|
|
6690
|
-
}
|
|
6691
|
-
},
|
|
6692
|
-
"uipath-json-similarity": {
|
|
6693
|
-
name: "JSON Similarity",
|
|
6694
|
-
description: "Compare JSON output similarity",
|
|
6695
|
-
category: "deterministic",
|
|
6696
|
-
shortDescription: "Passes when JSON structures are similar",
|
|
6697
|
-
configFields: [targetOutputKeyConfigField],
|
|
6698
|
-
criteriaFields: [expectedOutputCriteriaField],
|
|
6699
|
-
examples: {
|
|
6700
|
-
criteria: { expectedOutput: { status: "ok" } }
|
|
6701
|
-
}
|
|
6702
|
-
}
|
|
6703
|
-
};
|
|
6704
|
-
|
|
6705
|
-
// src/services/flow-eval-schema/llm-judge-evaluators.ts
|
|
6706
|
-
var LLM_JUDGE_EVALUATOR_TYPES = {
|
|
6707
|
-
"uipath-llm-judge-output-semantic-similarity": {
|
|
6708
|
-
name: "LLM Judge Output",
|
|
6709
|
-
description: "Use LLM to judge semantic similarity of outputs",
|
|
6710
|
-
category: "llm-judge",
|
|
6711
|
-
shortDescription: "AI judges if output semantically matches expected",
|
|
6712
|
-
configFields: [...llmBaseConfigFields, targetOutputKeyConfigField],
|
|
6713
|
-
criteriaFields: [expectedOutputCriteriaField],
|
|
6714
|
-
examples: {
|
|
6715
|
-
config: { model: "gpt-4o", temperature: 0 },
|
|
6716
|
-
criteria: {
|
|
6717
|
-
expectedOutput: {
|
|
6718
|
-
text: "The loan has been approved for the requested amount."
|
|
6719
|
-
}
|
|
6720
|
-
}
|
|
6721
|
-
}
|
|
6722
|
-
},
|
|
6723
|
-
"uipath-llm-judge-output-strict-json-similarity": {
|
|
6724
|
-
name: "LLM Judge Strict JSON",
|
|
6725
|
-
description: "Use LLM to judge strict JSON similarity",
|
|
6726
|
-
category: "llm-judge",
|
|
6727
|
-
shortDescription: "AI judges strict JSON structural match",
|
|
6728
|
-
configFields: [...llmBaseConfigFields, targetOutputKeyConfigField],
|
|
6729
|
-
criteriaFields: [expectedOutputCriteriaField],
|
|
6730
|
-
examples: {
|
|
6731
|
-
config: { model: "gpt-4o", temperature: 0 },
|
|
6732
|
-
criteria: { expectedOutput: { result: "success" } }
|
|
6733
|
-
}
|
|
6734
|
-
},
|
|
6735
|
-
"uipath-llm-judge-trajectory-similarity": {
|
|
6736
|
-
name: "LLM Judge Trajectory",
|
|
6737
|
-
description: "Use LLM to judge agent trajectory and behavior",
|
|
6738
|
-
category: "llm-judge",
|
|
6739
|
-
shortDescription: "AI judges if agent followed expected behavior",
|
|
6740
|
-
configFields: llmTrajectoryBaseConfigFields,
|
|
6741
|
-
criteriaFields: [
|
|
6742
|
-
{
|
|
6743
|
-
key: "expectedAgentBehavior",
|
|
6744
|
-
label: "Expected Agent Behavior",
|
|
6745
|
-
type: "string",
|
|
6746
|
-
required: false,
|
|
6747
|
-
description: "Description of expected agent behavior and actions"
|
|
6748
|
-
}
|
|
6749
|
-
],
|
|
6750
|
-
examples: {
|
|
6751
|
-
config: { model: "gpt-4o" },
|
|
6752
|
-
criteria: {
|
|
6753
|
-
expectedAgentBehavior: "Agent checks eligibility, then calls approval API"
|
|
6754
|
-
}
|
|
6755
|
-
}
|
|
6756
|
-
},
|
|
6757
|
-
"uipath-llm-judge-trajectory-simulation": {
|
|
6758
|
-
name: "LLM Judge Simulation",
|
|
6759
|
-
description: "Use LLM to judge simulation trajectory",
|
|
6760
|
-
category: "llm-judge",
|
|
6761
|
-
shortDescription: "AI judges simulated agent execution path",
|
|
6762
|
-
configFields: llmTrajectoryBaseConfigFields,
|
|
6763
|
-
criteriaFields: [
|
|
6764
|
-
{
|
|
6765
|
-
key: "expectedAgentBehavior",
|
|
6766
|
-
label: "Expected Agent Behavior",
|
|
6767
|
-
type: "string",
|
|
6768
|
-
required: false,
|
|
6769
|
-
description: "Description of expected agent behavior during simulation"
|
|
6770
|
-
}
|
|
6771
|
-
],
|
|
6772
|
-
examples: {
|
|
6773
|
-
config: { model: "gpt-4o" },
|
|
6774
|
-
criteria: {
|
|
6775
|
-
expectedAgentBehavior: "Agent simulates the user journey through checkout flow"
|
|
6776
|
-
}
|
|
6777
|
-
}
|
|
6778
|
-
}
|
|
6779
|
-
};
|
|
6780
|
-
|
|
6781
|
-
// src/services/flow-eval-schema/tool-call-evaluators.ts
|
|
6782
|
-
var TOOL_CALL_EVALUATOR_TYPES = {
|
|
6783
|
-
"uipath-tool-call-args": {
|
|
6784
|
-
name: "Tool Call Arguments",
|
|
6785
|
-
description: "Verify tool call arguments match expected values",
|
|
6786
|
-
category: "tool-call",
|
|
6787
|
-
shortDescription: "Checks that tools were called with correct arguments",
|
|
6788
|
-
configFields: [
|
|
6789
|
-
{
|
|
6790
|
-
key: "strict",
|
|
6791
|
-
label: "Strict Mode",
|
|
6792
|
-
type: "boolean",
|
|
6793
|
-
required: false,
|
|
6794
|
-
default: false,
|
|
6795
|
-
description: "If true, requires exact match of all tool call arguments"
|
|
6796
|
-
},
|
|
6797
|
-
{
|
|
6798
|
-
key: "subset",
|
|
6799
|
-
label: "Subset Mode",
|
|
6800
|
-
type: "boolean",
|
|
6801
|
-
required: false,
|
|
6802
|
-
default: false,
|
|
6803
|
-
description: "If true, allows actual tool calls to be a subset of expected"
|
|
6804
|
-
}
|
|
6805
|
-
],
|
|
6806
|
-
criteriaFields: [
|
|
6807
|
-
{
|
|
6808
|
-
key: "toolCalls",
|
|
6809
|
-
label: "Tool Calls",
|
|
6810
|
-
type: "toolCallArgs",
|
|
6811
|
-
required: false,
|
|
6812
|
-
description: "Expected tool calls with names and arguments"
|
|
6813
|
-
}
|
|
6814
|
-
]
|
|
6815
|
-
},
|
|
6816
|
-
"uipath-tool-call-count": {
|
|
6817
|
-
name: "Tool Call Count",
|
|
6818
|
-
description: "Verify tool call counts match expected values",
|
|
6819
|
-
category: "tool-call",
|
|
6820
|
-
shortDescription: "Checks number of times each tool was called",
|
|
6821
|
-
configFields: [
|
|
6822
|
-
{
|
|
6823
|
-
key: "strict",
|
|
6824
|
-
label: "Strict Mode",
|
|
6825
|
-
type: "boolean",
|
|
6826
|
-
required: false,
|
|
6827
|
-
default: false,
|
|
6828
|
-
description: "If true, requires exact count match"
|
|
6829
|
-
}
|
|
6830
|
-
],
|
|
6831
|
-
criteriaFields: [
|
|
6832
|
-
{
|
|
6833
|
-
key: "toolCallsCount",
|
|
6834
|
-
label: "Tool Calls Count",
|
|
6835
|
-
type: "toolCallCount",
|
|
6836
|
-
required: false,
|
|
6837
|
-
description: "Expected tool call counts with comparison operators"
|
|
6838
|
-
}
|
|
6839
|
-
]
|
|
6840
|
-
},
|
|
6841
|
-
"uipath-tool-call-order": {
|
|
6842
|
-
name: "Tool Call Order",
|
|
6843
|
-
description: "Verify tool calls occur in expected order",
|
|
6844
|
-
category: "tool-call",
|
|
6845
|
-
shortDescription: "Checks that tools were called in the right sequence",
|
|
6846
|
-
configFields: [
|
|
6847
|
-
{
|
|
6848
|
-
key: "strict",
|
|
6849
|
-
label: "Strict Mode",
|
|
6850
|
-
type: "boolean",
|
|
6851
|
-
required: false,
|
|
6852
|
-
default: false,
|
|
6853
|
-
description: "If true, requires exact order match"
|
|
6854
|
-
}
|
|
6855
|
-
],
|
|
6856
|
-
criteriaFields: [
|
|
6857
|
-
{
|
|
6858
|
-
key: "toolCallsOrder",
|
|
6859
|
-
label: "Tool Calls Order",
|
|
6860
|
-
type: "toolCallOrder",
|
|
6861
|
-
required: false,
|
|
6862
|
-
description: "Tool names in expected execution order"
|
|
6863
|
-
}
|
|
6864
|
-
]
|
|
6865
|
-
},
|
|
6866
|
-
"uipath-tool-call-output": {
|
|
6867
|
-
name: "Tool Call Output",
|
|
6868
|
-
description: "Verify tool call outputs match expected values",
|
|
6869
|
-
category: "tool-call",
|
|
6870
|
-
shortDescription: "Checks that tool outputs match expected values",
|
|
6871
|
-
configFields: [
|
|
6872
|
-
{
|
|
6873
|
-
key: "strict",
|
|
6874
|
-
label: "Strict Mode",
|
|
6875
|
-
type: "boolean",
|
|
6876
|
-
required: false,
|
|
6877
|
-
default: false,
|
|
6878
|
-
description: "If true, requires exact output match"
|
|
6879
|
-
}
|
|
6880
|
-
],
|
|
6881
|
-
criteriaFields: [
|
|
6882
|
-
{
|
|
6883
|
-
key: "toolOutputs",
|
|
6884
|
-
label: "Tool Outputs",
|
|
6885
|
-
type: "toolCallOutput",
|
|
6886
|
-
required: false,
|
|
6887
|
-
description: "Expected tool outputs with names and output values"
|
|
6888
|
-
}
|
|
6889
|
-
]
|
|
6890
|
-
}
|
|
6891
|
-
};
|
|
6892
|
-
|
|
6893
|
-
// src/services/flow-eval-schema/registry.ts
|
|
6894
|
-
var EVALUATOR_TYPE_REGISTRY = {
|
|
6895
|
-
...DETERMINISTIC_EVALUATOR_TYPES,
|
|
6896
|
-
...LLM_JUDGE_EVALUATOR_TYPES,
|
|
6897
|
-
...TOOL_CALL_EVALUATOR_TYPES
|
|
6898
|
-
};
|
|
6899
|
-
var EVALUATOR_TYPE_ALIASES = {
|
|
6900
|
-
"exact-match": "uipath-exact-match",
|
|
6901
|
-
"json-similarity": "uipath-json-similarity",
|
|
6902
|
-
contains: "uipath-contains",
|
|
6903
|
-
"llm-judge-output": "uipath-llm-judge-output-semantic-similarity",
|
|
6904
|
-
"llm-judge-strict-json": "uipath-llm-judge-output-strict-json-similarity",
|
|
6905
|
-
"llm-judge-trajectory": "uipath-llm-judge-trajectory-similarity",
|
|
6906
|
-
"llm-judge-trajectory-simulation": "uipath-llm-judge-trajectory-simulation"
|
|
6907
|
-
};
|
|
6908
|
-
|
|
6909
|
-
// src/services/flow-eval-schema/helpers.ts
|
|
6910
|
-
function evaluatorTypeAliases() {
|
|
6911
|
-
return Object.keys(EVALUATOR_TYPE_ALIASES);
|
|
6912
|
-
}
|
|
6913
|
-
function isEvaluatorTypeId(value) {
|
|
6914
|
-
return value in EVALUATOR_TYPE_REGISTRY;
|
|
6915
|
-
}
|
|
6916
|
-
function getEvaluatorTypeDefinition(typeId) {
|
|
6917
|
-
if (!isEvaluatorTypeId(typeId)) {
|
|
6918
|
-
return null;
|
|
6919
|
-
}
|
|
6920
|
-
return EVALUATOR_TYPE_REGISTRY[typeId];
|
|
6921
|
-
}
|
|
6922
|
-
function resolveEvaluatorType(type) {
|
|
6923
|
-
const resolved = EVALUATOR_TYPE_ALIASES[type] ?? type;
|
|
6924
|
-
if (!isEvaluatorTypeId(resolved)) {
|
|
6925
|
-
throw new Error(`Unknown evaluator type "${type}". Valid types: ${evaluatorTypeAliases().join(", ")}`);
|
|
6926
|
-
}
|
|
6927
|
-
return resolved;
|
|
6928
|
-
}
|
|
6929
|
-
function getDefaultConfigForEvaluatorType(typeId) {
|
|
6930
|
-
return collectFieldDefaults(EVALUATOR_TYPE_REGISTRY[typeId].configFields);
|
|
6931
|
-
}
|
|
6932
|
-
function collectFieldDefaults(fields) {
|
|
6933
|
-
const defaults = {};
|
|
6934
|
-
for (const field of fields) {
|
|
6935
|
-
if (field.default !== undefined) {
|
|
6936
|
-
defaults[field.key] = field.default;
|
|
6937
|
-
}
|
|
6938
|
-
}
|
|
6939
|
-
return defaults;
|
|
6940
|
-
}
|
|
6941
|
-
function evaluatorTypeHasConfigField(typeId, fieldKey) {
|
|
6942
|
-
return getEvaluatorTypeDefinition(typeId)?.configFields.some((field) => field.key === fieldKey) ?? false;
|
|
6943
|
-
}
|
|
6944
|
-
function evaluatorCanUseSharedExpectedOutput(typeId) {
|
|
6945
|
-
return getEvaluatorTypeDefinition(typeId)?.criteriaFields.some((field) => field.key === "expectedOutput" && field.type === "object") ?? false;
|
|
6946
|
-
}
|
|
6947
|
-
// src/services/flow-eval-utils.ts
|
|
6948
|
-
function isRecord(value) {
|
|
6949
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6950
|
-
}
|
|
6951
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6952
|
-
function isUUID(value) {
|
|
6953
|
-
return UUID_RE.test(value);
|
|
6954
|
-
}
|
|
6955
|
-
function stripJsonExt(name) {
|
|
6956
|
-
return name.endsWith(".json") ? name.slice(0, -".json".length) : name;
|
|
6957
|
-
}
|
|
6958
|
-
function slugify(text) {
|
|
6959
|
-
const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
6960
|
-
return slug || "untitled";
|
|
6961
|
-
}
|
|
6962
|
-
function getEvaluatorRef(evaluator) {
|
|
6963
|
-
return stripJsonExt(evaluator.fileName ?? `${evaluator.id}.json`);
|
|
6964
|
-
}
|
|
6965
|
-
function buildEvaluatorConfig(typeId, options) {
|
|
6966
|
-
const evaluatorConfig = {
|
|
6967
|
-
name: options.name,
|
|
6968
|
-
...getDefaultConfigForEvaluatorType(typeId)
|
|
6969
|
-
};
|
|
6970
|
-
if (evaluatorTypeHasConfigField(typeId, "targetOutputKey")) {
|
|
6971
|
-
evaluatorConfig.targetOutputKey = options.targetOutputKey ?? "*";
|
|
6972
|
-
}
|
|
6973
|
-
if (options.model !== undefined) {
|
|
6974
|
-
evaluatorConfig.model = options.model;
|
|
6975
|
-
}
|
|
6976
|
-
if (options.prompt !== undefined) {
|
|
6977
|
-
evaluatorConfig.prompt = options.prompt;
|
|
6978
|
-
}
|
|
6979
|
-
return evaluatorConfig;
|
|
6980
|
-
}
|
|
6981
|
-
function buildDefaultCriteria(evaluators, expectedOutput, searchText) {
|
|
6982
|
-
const criterias = {};
|
|
6983
|
-
for (const evaluator of evaluators) {
|
|
6984
|
-
const key = evaluator.id;
|
|
6985
|
-
if (expectedOutput !== undefined && evaluatorCanUseSharedExpectedOutput(evaluator.evaluatorTypeId)) {
|
|
6986
|
-
criterias[key] = { expectedOutput };
|
|
6987
|
-
} else if (searchText !== undefined && evaluator.evaluatorTypeId === "uipath-contains") {
|
|
6988
|
-
criterias[key] = { searchText };
|
|
6989
|
-
} else {
|
|
6990
|
-
const defaultCriteria = evaluator.evaluatorConfig.defaultEvaluationCriteria;
|
|
6991
|
-
criterias[key] = isRecord(defaultCriteria) ? defaultCriteria : null;
|
|
6992
|
-
}
|
|
6993
|
-
}
|
|
6994
|
-
return criterias;
|
|
6995
|
-
}
|
|
6996
|
-
|
|
6997
|
-
// src/services/flow-migration-check.ts
|
|
6998
|
-
if (!instance.isInitialized) {
|
|
6999
|
-
instance.init({
|
|
7000
|
-
lng: "en",
|
|
7001
|
-
fallbackLng: "en",
|
|
7002
|
-
defaultNS: "validation",
|
|
7003
|
-
resources: {},
|
|
7004
|
-
interpolation: { escapeValue: false }
|
|
7005
|
-
});
|
|
7006
|
-
}
|
|
7007
|
-
registerFlowMigrationsI18n(instance);
|
|
7008
|
-
function captureMigrationIssues() {
|
|
7009
|
-
const paths = [];
|
|
7010
|
-
const tracker = {
|
|
7011
|
-
trackError: (_error, _severity, details) => {
|
|
7012
|
-
if (!("issues" in details))
|
|
7013
|
-
return;
|
|
7014
|
-
const issues = details.issues;
|
|
7015
|
-
if (!Array.isArray(issues))
|
|
7016
|
-
return;
|
|
7017
|
-
for (const issue of issues) {
|
|
7018
|
-
if (!issue || typeof issue !== "object")
|
|
7019
|
-
continue;
|
|
7020
|
-
if (!("path" in issue))
|
|
7021
|
-
continue;
|
|
7022
|
-
const path = issue.path;
|
|
7023
|
-
if (Array.isArray(path) && path.length > 0) {
|
|
7024
|
-
paths.push(path.map(String).join("."));
|
|
7025
|
-
}
|
|
7026
|
-
}
|
|
7027
|
-
}
|
|
7028
|
-
};
|
|
7029
|
-
return { tracker, fieldPaths: () => [...new Set(paths)] };
|
|
7030
|
-
}
|
|
7031
|
-
function formatMigrationFailureMessage(step, rawMessage, fieldPaths) {
|
|
7032
|
-
const parts = [`Workflow migration failed at ${step}: ${rawMessage}.`];
|
|
7033
|
-
if (fieldPaths.length > 0) {
|
|
7034
|
-
const shown = fieldPaths.slice(0, 10);
|
|
7035
|
-
const extra = fieldPaths.length > shown.length ? `, +${fieldPaths.length - shown.length} more` : "";
|
|
7036
|
-
parts.push(`Offending field(s): ${shown.join(", ")}${extra}.`);
|
|
7037
|
-
}
|
|
7038
|
-
parts.push("Fix the flagged field(s) or re-scaffold the affected node, then re-run.");
|
|
7039
|
-
return parts.join(" ");
|
|
7040
|
-
}
|
|
7041
|
-
function checkForwardMigration(rawWorkflow) {
|
|
7042
|
-
try {
|
|
7043
|
-
getMigrationChain(runtimeMigrations, String(rawWorkflow.version ?? ""), runtimeVersion);
|
|
7044
|
-
} catch {
|
|
7045
|
-
return { ok: true };
|
|
7046
|
-
}
|
|
7047
|
-
const { tracker, fieldPaths } = captureMigrationIssues();
|
|
7048
|
-
const result = migrateWorkflowToFloor(runtimeMigrations, rawWorkflow, runtimeVersion, minimumSupportedVersion, tracker);
|
|
7049
|
-
if (!result.error)
|
|
7050
|
-
return { ok: true };
|
|
7051
|
-
const paths = fieldPaths();
|
|
7052
|
-
return {
|
|
7053
|
-
ok: false,
|
|
7054
|
-
step: result.error.step,
|
|
7055
|
-
rawMessage: result.error.message,
|
|
7056
|
-
message: formatMigrationFailureMessage(result.error.step, result.error.message, paths),
|
|
7057
|
-
fieldPaths: paths
|
|
7058
|
-
};
|
|
7059
|
-
}
|
|
7060
|
-
|
|
7061
|
-
// src/services/flow-eval-file-store.ts
|
|
7062
|
-
async function ensureDirectory(fs, path) {
|
|
7063
|
-
if (await fs.exists(path)) {
|
|
7064
|
-
return;
|
|
7065
|
-
}
|
|
7066
|
-
const parent = fs.path.dirname(path);
|
|
7067
|
-
if (parent && parent !== path && !await fs.exists(parent)) {
|
|
7068
|
-
await ensureDirectory(fs, parent);
|
|
7069
|
-
}
|
|
7070
|
-
const [error] = await catchError(fs.mkdir(path));
|
|
7071
|
-
if (error && !await fs.exists(path)) {
|
|
7072
|
-
throw error;
|
|
7073
|
-
}
|
|
7074
|
-
}
|
|
7075
|
-
|
|
7076
|
-
class FlowEvalFileStore {
|
|
7077
|
-
fs;
|
|
7078
|
-
constructor(fs) {
|
|
7079
|
-
this.fs = fs;
|
|
7080
|
-
}
|
|
7081
|
-
async readJsonFiles(dir) {
|
|
7082
|
-
const [readDirError, files] = await catchError(this.fs.readdir(dir));
|
|
7083
|
-
if (readDirError) {
|
|
7084
|
-
return [];
|
|
7085
|
-
}
|
|
7086
|
-
const items = [];
|
|
7087
|
-
for (const file of files) {
|
|
7088
|
-
if (!file.endsWith(".json")) {
|
|
7089
|
-
continue;
|
|
7090
|
-
}
|
|
7091
|
-
const filePath = this.fs.path.join(dir, file);
|
|
7092
|
-
const [readError, raw] = await catchError(this.fs.readFile(filePath, "utf-8"));
|
|
7093
|
-
if (readError) {
|
|
7094
|
-
throw new Error(`Failed to read JSON file "${filePath}": ${readError.message}`);
|
|
7095
|
-
}
|
|
7096
|
-
if (raw === null) {
|
|
7097
|
-
continue;
|
|
7098
|
-
}
|
|
7099
|
-
const [parseError, data2] = catchError(() => JSON.parse(String(raw)));
|
|
7100
|
-
if (parseError) {
|
|
7101
|
-
throw new Error(`Invalid JSON in "${filePath}": ${parseError.message}`);
|
|
7102
|
-
}
|
|
7103
|
-
data2.fileName = file;
|
|
7104
|
-
items.push(data2);
|
|
7105
|
-
}
|
|
7106
|
-
return items;
|
|
7107
|
-
}
|
|
7108
|
-
async writeJsonFile(path, value) {
|
|
7109
|
-
await ensureDirectory(this.fs, this.fs.path.dirname(path));
|
|
7110
|
-
await this.fs.writeFile(path, `${JSON.stringify(value, null, 2)}
|
|
7111
|
-
`);
|
|
7112
|
-
}
|
|
7113
|
-
async remove(path) {
|
|
7114
|
-
await this.fs.rm(path);
|
|
7115
|
-
}
|
|
7116
|
-
}
|
|
7117
|
-
|
|
7118
|
-
// src/services/packaging-utils.ts
|
|
7119
|
-
function fileNodesToPackagingNodes(nodes) {
|
|
7120
|
-
return nodes.map((node) => ({
|
|
7121
|
-
id: node.id,
|
|
7122
|
-
type: node.type,
|
|
7123
|
-
typeVersion: node.typeVersion ?? "1.0",
|
|
7124
|
-
display: node.display,
|
|
7125
|
-
inputs: node.inputs,
|
|
7126
|
-
outputs: node.outputs,
|
|
7127
|
-
...node.model ? { model: node.model } : {}
|
|
7128
|
-
}));
|
|
7129
|
-
}
|
|
7130
|
-
function isRecord2(value) {
|
|
7131
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7132
|
-
}
|
|
7133
|
-
function toResourceList(value) {
|
|
7134
|
-
if (!Array.isArray(value))
|
|
7135
|
-
return;
|
|
7136
|
-
const resources = value.filter(isRecord2);
|
|
7137
|
-
return resources.length === value.length ? resources : undefined;
|
|
7138
|
-
}
|
|
7139
|
-
function normalizePackagingBindingsJson(value) {
|
|
7140
|
-
if (!isRecord2(value))
|
|
7141
|
-
return;
|
|
7142
|
-
const resources = toResourceList(value.resources);
|
|
7143
|
-
if (!resources)
|
|
7144
|
-
return;
|
|
7145
|
-
return {
|
|
7146
|
-
...value,
|
|
7147
|
-
version: typeof value.version === "string" ? value.version : "2.0",
|
|
7148
|
-
resources
|
|
7149
|
-
};
|
|
7150
|
-
}
|
|
7151
|
-
function emptyPackagingBindingsJson() {
|
|
7152
|
-
return { version: "2.0", resources: [] };
|
|
7153
|
-
}
|
|
7154
|
-
function mergePackagingBindingsJson(bindingsJsons) {
|
|
7155
|
-
const first = bindingsJsons.find((bindingsJson) => bindingsJson !== undefined);
|
|
7156
|
-
return {
|
|
7157
|
-
...first ?? emptyPackagingBindingsJson(),
|
|
7158
|
-
resources: mergeBindingResources(bindingsJsons.flatMap((bindingsJson) => bindingsJson?.resources ?? []))
|
|
7159
|
-
};
|
|
7160
|
-
}
|
|
7161
|
-
var inlineAgentPackagerLogger = {
|
|
7162
|
-
debug: (message) => logger.debug(message),
|
|
7163
|
-
info: (message) => logger.info(message),
|
|
7164
|
-
warn: (message) => logger.warn(message),
|
|
7165
|
-
error: (message) => logger.error(message),
|
|
7166
|
-
progress: (message) => logger.info(message),
|
|
7167
|
-
log: (message, level) => {
|
|
7168
|
-
if (level === "Error" /* Error */)
|
|
7169
|
-
logger.error(message);
|
|
7170
|
-
else if (level === "Warning" /* Warn */)
|
|
7171
|
-
logger.warn(message);
|
|
7172
|
-
else if (level === "Debug" /* Debug */)
|
|
7173
|
-
logger.debug(message);
|
|
7174
|
-
else
|
|
7175
|
-
logger.info(message);
|
|
7176
|
-
},
|
|
7177
|
-
logMessage: (message) => inlineAgentPackagerLogger.log(message.message, message.logLevel)
|
|
7178
|
-
};
|
|
7179
|
-
async function createInlineAgentContract(fs, agentDir) {
|
|
7180
|
-
const outputPath = fs.path.join(fs.env.tmpdir(), `.inline-agent-contract-${crypto.randomUUID()}`);
|
|
7181
|
-
await fs.mkdir(outputPath);
|
|
7182
|
-
try {
|
|
7183
|
-
const built = await buildInlineAgentContract({
|
|
7184
|
-
fileSystem: fs,
|
|
7185
|
-
logger: inlineAgentPackagerLogger,
|
|
7186
|
-
projectPath: agentDir,
|
|
7187
|
-
outputPath
|
|
7188
|
-
});
|
|
7189
|
-
const bindingsJson = normalizePackagingBindingsJson(built.bindingsJson);
|
|
7190
|
-
if (!bindingsJson) {
|
|
7191
|
-
throw new Error("Agent packager returned an invalid binding contract");
|
|
7192
|
-
}
|
|
7193
|
-
return { agentJson: built.agentJson, bindingsJson };
|
|
7194
|
-
} finally {
|
|
7195
|
-
const [cleanupError] = await catchError(fs.rm(outputPath, { recursive: true }));
|
|
7196
|
-
if (cleanupError) {
|
|
7197
|
-
logger.debug(`Could not remove inline-agent contract directory ${outputPath}: ${cleanupError.message}`);
|
|
7198
|
-
}
|
|
7199
|
-
}
|
|
7200
|
-
}
|
|
7201
|
-
async function inlineAgentExists(fs, projectDir, source) {
|
|
7202
|
-
const agentJsonPath = fs.path.join(projectDir, source, "agent.json");
|
|
7203
|
-
return fs.exists(agentJsonPath);
|
|
7204
|
-
}
|
|
7205
|
-
async function packageInlineAgents(fs, projectDir, nodes) {
|
|
7206
|
-
const result = [];
|
|
7207
|
-
for (const node of nodes) {
|
|
7208
|
-
if (!isInlineAgentNodeType(node.type))
|
|
7209
|
-
continue;
|
|
7210
|
-
const source = readInlineAgentSource(node);
|
|
7211
|
-
if (!source)
|
|
7212
|
-
continue;
|
|
7213
|
-
if (/[/\\.]/.test(source))
|
|
7214
|
-
continue;
|
|
7215
|
-
if (!await inlineAgentExists(fs, projectDir, source))
|
|
7216
|
-
continue;
|
|
7217
|
-
const contract = await createInlineAgentContract(fs, fs.path.join(projectDir, source));
|
|
7218
|
-
const inputSchema = contract.agentJson.inputSchema ?? {
|
|
7219
|
-
type: "object",
|
|
7220
|
-
properties: {}
|
|
7221
|
-
};
|
|
7222
|
-
const outputSchema = contract.agentJson.outputSchema ?? {
|
|
7223
|
-
type: "object",
|
|
7224
|
-
properties: {}
|
|
7225
|
-
};
|
|
7226
|
-
result.push({
|
|
7227
|
-
source,
|
|
7228
|
-
entryPoint: {
|
|
7229
|
-
filePath: `content/${source}/agent.json`,
|
|
7230
|
-
uniqueId: contract.agentJson.projectId ?? source,
|
|
7231
|
-
type: EntryPointType.Agent,
|
|
7232
|
-
input: inputSchema,
|
|
7233
|
-
output: outputSchema,
|
|
7234
|
-
displayName: contract.agentJson.name ?? "Agent"
|
|
7235
|
-
},
|
|
7236
|
-
agentJson: contract.agentJson,
|
|
7237
|
-
bindingsJson: contract.bindingsJson
|
|
7238
|
-
});
|
|
7239
|
-
logger.info(`Packaged inline agent "${source}" for entry-points`);
|
|
7240
|
-
}
|
|
7241
|
-
return result;
|
|
7242
|
-
}
|
|
7243
|
-
async function stageInlineAgentPackageFiles(fs, sourceProjectPath, stagingProjectDir, inlineAgents, formatLogMessage) {
|
|
7244
|
-
for (const agent of inlineAgents) {
|
|
7245
|
-
const srcDir = fs.path.join(sourceProjectPath, agent.source);
|
|
7246
|
-
const destDir = fs.path.join(stagingProjectDir, agent.source);
|
|
7247
|
-
if (fs.path.resolve(srcDir) !== fs.path.resolve(destDir) && await fs.exists(srcDir)) {
|
|
7248
|
-
await fs.copyDirectory(srcDir, destDir);
|
|
7249
|
-
}
|
|
7250
|
-
const agentBuilderDir = fs.path.join(destDir, ".agent-builder");
|
|
7251
|
-
await ensureDirectory(fs, agentBuilderDir);
|
|
7252
|
-
await fs.writeFile(fs.path.join(agentBuilderDir, "agent.json"), JSON.stringify({
|
|
7253
|
-
...agent.agentJson,
|
|
7254
|
-
resources: agent.agentJson.resources ?? [],
|
|
7255
|
-
features: agent.agentJson.features ?? []
|
|
7256
|
-
}, null, 2));
|
|
7257
|
-
await fs.writeFile(fs.path.join(agentBuilderDir, "bindings.json"), `${JSON.stringify(agent.bindingsJson ?? emptyPackagingBindingsJson(), null, 2)}
|
|
7258
|
-
`);
|
|
7259
|
-
logger.info(formatLogMessage(agent.source));
|
|
7260
|
-
}
|
|
7261
|
-
}
|
|
7262
|
-
function buildInlineAgentDescriptors(nodes) {
|
|
7263
|
-
const out = [];
|
|
7264
|
-
for (const node of nodes) {
|
|
7265
|
-
if (!isInlineAgentNodeType(node.type))
|
|
7266
|
-
continue;
|
|
7267
|
-
const source = readInlineAgentSource(node);
|
|
7268
|
-
if (!source)
|
|
7269
|
-
continue;
|
|
7270
|
-
const label = node.display?.label ?? "Agent";
|
|
7271
|
-
out.push({
|
|
7272
|
-
name: label,
|
|
7273
|
-
agentProjectId: source,
|
|
7274
|
-
isConversational: isConversationalOrVoiceAgentNodeType(node.type),
|
|
7275
|
-
entryPointPath: `${source}/agent.json`
|
|
7276
|
-
});
|
|
7277
|
-
}
|
|
7278
|
-
return out;
|
|
7279
|
-
}
|
|
7280
|
-
async function writePackagingArtifacts(fs, projectDir, projectId, packagingNodes, bindings, variables, bpmnFileName, startEventId, flowFileName, definitions = [], options = {}) {
|
|
7281
|
-
const { entryPoints } = await writePackagingArtifactsDetailed(fs, projectDir, projectId, packagingNodes, bindings, variables, bpmnFileName, startEventId, flowFileName, definitions, options);
|
|
7282
|
-
return entryPoints;
|
|
7283
|
-
}
|
|
7284
|
-
async function writePackagingArtifactsDetailed(fs, projectDir, projectId, packagingNodes, bindings, variables, bpmnFileName, startEventId, flowFileName, definitions = [], options = {}) {
|
|
7285
|
-
const flowEntryPoints = getEntryPoints(bpmnFileName, packagingNodes, variables, definitions, ProjectType.Flow);
|
|
7286
|
-
const entryPoints = [
|
|
7287
|
-
...flowEntryPoints,
|
|
7288
|
-
...options.additionalEntryPoints ?? []
|
|
7289
|
-
];
|
|
7290
|
-
await fs.writeFile(fs.path.join(projectDir, "entry-points.json"), `${JSON.stringify(generateEntryPointsJson(entryPoints), null, 2)}
|
|
7291
|
-
`);
|
|
7292
|
-
const bindingResources = getBindingResources(packagingNodes, bindings, definitions);
|
|
7293
|
-
const bindingsJson = normalizePackagingBindingsJson(generateBindingsJson(bindingResources)) ?? emptyPackagingBindingsJson();
|
|
7294
|
-
const stagedBindingsJson = mergePackagingBindingsJson([
|
|
7295
|
-
bindingsJson,
|
|
7296
|
-
...options.additionalBindingsJsons ?? []
|
|
7297
|
-
]);
|
|
7298
|
-
await fs.writeFile(fs.path.join(projectDir, "bindings_v2.json"), `${JSON.stringify(stagedBindingsJson, null, 2)}
|
|
7299
|
-
`);
|
|
7300
|
-
const mainEntryPoint = `/${bpmnFileName}#${startEventId}`;
|
|
7301
|
-
const operateJson = generateOperateJson(projectId, mainEntryPoint, operateRuntimeOptions(packagingNodes));
|
|
7302
|
-
await fs.writeFile(fs.path.join(projectDir, "operate.json"), `${JSON.stringify(operateJson, null, 2)}
|
|
7303
|
-
`);
|
|
7304
|
-
const packageDescriptorJson = options.packageDescriptorJson ?? {
|
|
7305
|
-
$schema: "https://cloud.uipath.com/draft/2024-12/package-descriptor",
|
|
7306
|
-
files: {
|
|
7307
|
-
"operate.json": "operate.json",
|
|
7308
|
-
"entry-points.json": "entry-points.json",
|
|
7309
|
-
"bindings.json": "bindings_v2.json",
|
|
7310
|
-
[bpmnFileName]: bpmnFileName,
|
|
7311
|
-
...flowFileName ? { [flowFileName]: flowFileName } : {}
|
|
6397
|
+
if (obj && typeof obj === "object") {
|
|
6398
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
6399
|
+
scanForInvalidJsExpressions(value, `${path}.${key}`, severity, issues);
|
|
7312
6400
|
}
|
|
7313
|
-
}
|
|
7314
|
-
await fs.writeFile(fs.path.join(projectDir, "package-descriptor.json"), `${JSON.stringify(packageDescriptorJson, null, 2)}
|
|
7315
|
-
`);
|
|
7316
|
-
return { entryPoints, bindingsJson: stagedBindingsJson };
|
|
6401
|
+
}
|
|
7317
6402
|
}
|
|
7318
|
-
function
|
|
7319
|
-
if (
|
|
7320
|
-
return
|
|
7321
|
-
const
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
}
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
return { ...n, model: def.model };
|
|
6403
|
+
function reportInvalidExpression(expression, path, severity, issues) {
|
|
6404
|
+
if (expression.trim() === "")
|
|
6405
|
+
return;
|
|
6406
|
+
const error = syntaxErrorFrom(() => parse3(`(${expression}
|
|
6407
|
+
)`, {
|
|
6408
|
+
ecmaVersion: 2020,
|
|
6409
|
+
sourceType: "script"
|
|
6410
|
+
}));
|
|
6411
|
+
if (!error)
|
|
6412
|
+
return;
|
|
6413
|
+
issues.push({
|
|
6414
|
+
path,
|
|
6415
|
+
severity,
|
|
6416
|
+
message: `Expression at this field is not valid JavaScript: ${error.message}. Fix the syntax — validate cannot otherwise catch JavaScript expression errors; they surface only at flow debug.`
|
|
7333
6417
|
});
|
|
7334
6418
|
}
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
}
|
|
7342
|
-
const [parseErr, flowData] = catchError(() => JSON.parse(content));
|
|
7343
|
-
if (parseErr) {
|
|
7344
|
-
throw new Error(`Failed to parse .flow file at ${absolutePath}: ${parseErr.message}`);
|
|
7345
|
-
}
|
|
7346
|
-
const packagingNodes = backfillNodeModelsFromDefinitions(fileNodesToPackagingNodes(flowData.nodes ?? []), flowData.definitions ?? []);
|
|
7347
|
-
const bindingResources = getBindingResources(packagingNodes, flowData.bindings ?? [], flowData.definitions ?? []);
|
|
7348
|
-
await fs.writeFile(fs.path.join(projectDir, "bindings_v2.json"), `${JSON.stringify(generateBindingsJson(bindingResources), null, 2)}
|
|
7349
|
-
`);
|
|
7350
|
-
}
|
|
7351
|
-
|
|
7352
|
-
// src/services/node-service.ts
|
|
7353
|
-
class NodeTypeNotFoundError extends Error {
|
|
7354
|
-
code = "NodeTypeNotFound";
|
|
7355
|
-
instructions;
|
|
7356
|
-
constructor(message, instructions) {
|
|
7357
|
-
super(message);
|
|
7358
|
-
this.name = "NodeTypeNotFoundError";
|
|
7359
|
-
this.instructions = instructions;
|
|
7360
|
-
}
|
|
7361
|
-
}
|
|
7362
|
-
|
|
7363
|
-
class NodeTypeNotEnabledError extends Error {
|
|
7364
|
-
code = "NodeTypeNotEnabled";
|
|
7365
|
-
instructions;
|
|
7366
|
-
constructor(message, instructions) {
|
|
7367
|
-
super(message);
|
|
7368
|
-
this.name = "NodeTypeNotEnabledError";
|
|
7369
|
-
this.instructions = instructions;
|
|
6419
|
+
function syntaxErrorFrom(parseSource) {
|
|
6420
|
+
try {
|
|
6421
|
+
parseSource();
|
|
6422
|
+
return;
|
|
6423
|
+
} catch (err) {
|
|
6424
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
7370
6425
|
}
|
|
7371
6426
|
}
|
|
7372
6427
|
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
6428
|
+
// src/services/node-validators/model-source-validator.ts
|
|
6429
|
+
var modelSourceValidator = {
|
|
6430
|
+
id: "model-source",
|
|
6431
|
+
appliesTo: () => true,
|
|
6432
|
+
validate: (node, context) => {
|
|
6433
|
+
const definition = context.definitions.find((d) => d.nodeType === node.type);
|
|
6434
|
+
if (!definition)
|
|
6435
|
+
return [];
|
|
6436
|
+
const requiresModelSource = readModelSourceFlag(definition);
|
|
6437
|
+
if (!requiresModelSource)
|
|
6438
|
+
return [];
|
|
6439
|
+
const legacySource = readLegacyModelSource(node);
|
|
6440
|
+
if (legacySource !== undefined) {
|
|
6441
|
+
return [
|
|
6442
|
+
{
|
|
6443
|
+
path: `nodes[${node.id}].model.source`,
|
|
6444
|
+
severity: "error",
|
|
6445
|
+
message: `Node type "${node.type}" carries the deprecated ` + `"model.source" UUID on the node instance. ` + `flow-core 0.2.50+ requires the canvas-generated ` + `UUID at "inputs.source"; remove "model.source" ` + `from the node and place the value under ` + `"inputs": { "source": "<uuid>" } instead. ` + `See uipath-maestro-flow skill — ` + `inline-agent/impl.md (MST-9265).`
|
|
6446
|
+
}
|
|
6447
|
+
];
|
|
6448
|
+
}
|
|
6449
|
+
const inputsSource = node.inputs?.source;
|
|
6450
|
+
if (typeof inputsSource === "string" && inputsSource.length > 0) {
|
|
6451
|
+
return [];
|
|
6452
|
+
}
|
|
6453
|
+
return [
|
|
6454
|
+
{
|
|
6455
|
+
path: `nodes[${node.id}].inputs.source`,
|
|
6456
|
+
severity: "error",
|
|
6457
|
+
message: `Node type "${node.type}" requires a stable UUID at ` + `"inputs.source" — the agent's ProjectId for inline ` + `agent nodes, or the resource id from the parent inline ` + `agent's "resources[]" for agent-tool resource nodes. ` + `Set "inputs": { "source": "<uuid>" } on the node ` + `(use the ProjectId returned by ` + `"uip agent init --inline-in-flow" for inline agents). ` + `See uipath-maestro-flow skill — inline-agent/impl.md ` + `(MST-9265).`
|
|
6458
|
+
}
|
|
6459
|
+
];
|
|
7380
6460
|
}
|
|
7381
|
-
}
|
|
7382
|
-
var listNodesInFlow = async (filePath) => {
|
|
7383
|
-
const { workflow } = await readFlowWorkflow(filePath);
|
|
7384
|
-
const nodes = workflow.nodes.map((n) => ({
|
|
7385
|
-
id: n.id,
|
|
7386
|
-
type: n.type,
|
|
7387
|
-
label: n.display.label ?? "",
|
|
7388
|
-
position: n.ui.position
|
|
7389
|
-
}));
|
|
7390
|
-
return { nodes, count: nodes.length };
|
|
7391
6461
|
};
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
"
|
|
7395
|
-
|
|
7396
|
-
"uipath.core.api-workflow."
|
|
7397
|
-
];
|
|
7398
|
-
function isProcessNode(manifest) {
|
|
7399
|
-
return PROCESS_NODE_PREFIXES.some((prefix) => manifest.nodeType?.startsWith(prefix));
|
|
7400
|
-
}
|
|
7401
|
-
function extractProcessGuid(nodeType) {
|
|
7402
|
-
const match = nodeType.match(/^(?:uipath\.core\.rpa-workflow|uipath\.agent\.resource\.tool\.process|uipath\.core\.agent|uipath\.core\.api-workflow)\.([0-9a-f-]+)$/i);
|
|
7403
|
-
if (!match) {
|
|
7404
|
-
throw new Error(`Invalid process node type: "${nodeType}". ` + "Expected a known prefix followed by a process GUID.");
|
|
7405
|
-
}
|
|
7406
|
-
return match[1];
|
|
7407
|
-
}
|
|
7408
|
-
function resolveProcessTarget(node, definitions) {
|
|
7409
|
-
if (!isProcessNode({ nodeType: node.type }))
|
|
7410
|
-
return;
|
|
7411
|
-
let subType = "Process";
|
|
7412
|
-
if (definitions) {
|
|
7413
|
-
const sameType = definitions.filter((d) => d.nodeType === node.type);
|
|
7414
|
-
const def = sameType.find((d) => node.typeVersion !== undefined && d.version === node.typeVersion) ?? sameType[0];
|
|
7415
|
-
const model = def?.model;
|
|
7416
|
-
const defBindings = model?.bindings;
|
|
7417
|
-
if (defBindings?.resourceSubType)
|
|
7418
|
-
subType = defBindings.resourceSubType;
|
|
7419
|
-
if (defBindings?.resourceKey) {
|
|
7420
|
-
return { key: defBindings.resourceKey, subType };
|
|
7421
|
-
}
|
|
7422
|
-
}
|
|
7423
|
-
return { key: extractProcessGuid(node.type), subType };
|
|
7424
|
-
}
|
|
7425
|
-
function resolveProcessTargetKey(node, definitions) {
|
|
7426
|
-
return resolveProcessTarget(node, definitions)?.key;
|
|
7427
|
-
}
|
|
7428
|
-
function createProcessBindings(manifest) {
|
|
7429
|
-
const processGuid = extractProcessGuid(manifest.nodeType);
|
|
7430
|
-
const model = manifest.model;
|
|
7431
|
-
const rawValues = model?.bindings?.values;
|
|
7432
|
-
let processName;
|
|
7433
|
-
let folderPath;
|
|
7434
|
-
if (Array.isArray(rawValues)) {
|
|
7435
|
-
processName = rawValues.find((v) => v.propertyAttribute === "name")?.default ?? "";
|
|
7436
|
-
folderPath = rawValues.find((v) => v.propertyAttribute === "folderPath")?.default;
|
|
7437
|
-
} else {
|
|
7438
|
-
processName = rawValues?.name ?? "";
|
|
7439
|
-
folderPath = rawValues?.folderPath;
|
|
7440
|
-
}
|
|
7441
|
-
if (folderPath == null) {
|
|
7442
|
-
throw new Error("Manifest is missing model.bindings.values.folderPath. " + "Cannot create process bindings without a folder path.");
|
|
7443
|
-
}
|
|
7444
|
-
const subType = model?.bindings?.resourceSubType ?? "Process";
|
|
7445
|
-
const resourceKey = model?.bindings?.resourceKey ?? processGuid;
|
|
7446
|
-
const nameBinding = createBinding({
|
|
7447
|
-
name: "name",
|
|
7448
|
-
value: processName,
|
|
7449
|
-
resource: "process",
|
|
7450
|
-
resourceKey,
|
|
7451
|
-
propertyAttribute: "name",
|
|
7452
|
-
resourceSubType: subType
|
|
7453
|
-
});
|
|
7454
|
-
const folderBinding = createBinding({
|
|
7455
|
-
name: "folderPath",
|
|
7456
|
-
value: folderPath,
|
|
7457
|
-
resource: "process",
|
|
7458
|
-
resourceKey,
|
|
7459
|
-
propertyAttribute: "folderPath",
|
|
7460
|
-
resourceSubType: subType
|
|
7461
|
-
});
|
|
7462
|
-
if (folderPath === "" && folderBinding.default === undefined) {
|
|
7463
|
-
folderBinding.default = "";
|
|
6462
|
+
function readModelSourceFlag(definition) {
|
|
6463
|
+
const model = definition.model;
|
|
6464
|
+
if (!model || typeof model !== "object" || Array.isArray(model)) {
|
|
6465
|
+
return false;
|
|
7464
6466
|
}
|
|
7465
|
-
|
|
6467
|
+
const source = model.source;
|
|
6468
|
+
return source === true;
|
|
7466
6469
|
}
|
|
7467
|
-
function
|
|
6470
|
+
function readLegacyModelSource(node) {
|
|
7468
6471
|
const model = node.model;
|
|
7469
|
-
if (!model
|
|
6472
|
+
if (!model || typeof model !== "object" || Array.isArray(model)) {
|
|
7470
6473
|
return;
|
|
7471
|
-
for (const entry of model.context) {
|
|
7472
|
-
if (entry.name === "name" && typeof entry.value === "string") {
|
|
7473
|
-
entry.default = nameBinding.default;
|
|
7474
|
-
entry.value = `=bindings.${nameBinding.id}`;
|
|
7475
|
-
} else if (entry.name === "folderPath" && typeof entry.value === "string") {
|
|
7476
|
-
entry.default = folderBinding.default;
|
|
7477
|
-
entry.value = `=bindings.${folderBinding.id}`;
|
|
7478
|
-
}
|
|
7479
6474
|
}
|
|
6475
|
+
const source = model.source;
|
|
6476
|
+
if (typeof source === "string" && source.length > 0) {
|
|
6477
|
+
return source;
|
|
6478
|
+
}
|
|
6479
|
+
return;
|
|
7480
6480
|
}
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
6481
|
+
|
|
6482
|
+
// src/services/node-validators/index.ts
|
|
6483
|
+
var defaultNodeValidators = [
|
|
6484
|
+
activityTypeIdValidator,
|
|
6485
|
+
ceqlQueryValidator,
|
|
6486
|
+
connectorNodeValidator,
|
|
6487
|
+
crossNodeBindingValidator,
|
|
6488
|
+
expressionPrefixValidator,
|
|
6489
|
+
jsExpressionSyntaxValidator,
|
|
6490
|
+
ixpNodeValidator,
|
|
6491
|
+
modelSourceValidator
|
|
6492
|
+
];
|
|
6493
|
+
|
|
6494
|
+
// src/services/vars-reference-rule.ts
|
|
6495
|
+
var VARS_REF_RE = /\$vars((?:\s*\??\.\s*[A-Za-z_$][\w$]*|\s*\[\s*(?:'[^']*'|"[^"]*")\s*\])+)/g;
|
|
6496
|
+
var VARS_STEP_RE = /(\??\.)\s*([A-Za-z_$][\w$]*)|\[\s*(?:'([^']*)'|"([^"]*)")\s*\]/g;
|
|
6497
|
+
var JS_EXPRESSION_PREFIX = "=js:";
|
|
6498
|
+
var ARRAY_MEMBERS = new Set([
|
|
6499
|
+
"length",
|
|
6500
|
+
"at",
|
|
6501
|
+
"concat",
|
|
6502
|
+
"copyWithin",
|
|
6503
|
+
"entries",
|
|
6504
|
+
"every",
|
|
6505
|
+
"fill",
|
|
6506
|
+
"filter",
|
|
6507
|
+
"find",
|
|
6508
|
+
"findIndex",
|
|
6509
|
+
"findLast",
|
|
6510
|
+
"findLastIndex",
|
|
6511
|
+
"flat",
|
|
6512
|
+
"flatMap",
|
|
6513
|
+
"forEach",
|
|
6514
|
+
"includes",
|
|
6515
|
+
"indexOf",
|
|
6516
|
+
"join",
|
|
6517
|
+
"keys",
|
|
6518
|
+
"lastIndexOf",
|
|
6519
|
+
"map",
|
|
6520
|
+
"pop",
|
|
6521
|
+
"push",
|
|
6522
|
+
"reduce",
|
|
6523
|
+
"reduceRight",
|
|
6524
|
+
"reverse",
|
|
6525
|
+
"shift",
|
|
6526
|
+
"slice",
|
|
6527
|
+
"some",
|
|
6528
|
+
"sort",
|
|
6529
|
+
"splice",
|
|
6530
|
+
"toLocaleString",
|
|
6531
|
+
"toReversed",
|
|
6532
|
+
"toSorted",
|
|
6533
|
+
"toSpliced",
|
|
6534
|
+
"toString",
|
|
6535
|
+
"unshift",
|
|
6536
|
+
"values",
|
|
6537
|
+
"with"
|
|
6538
|
+
]);
|
|
6539
|
+
var LOOP_NODE_TYPE = "core.logic.loop";
|
|
6540
|
+
var LOOP_BACK_TARGET_PORT = "loopBack";
|
|
6541
|
+
var SCRIPT_NODE_TYPE = "core.action.script";
|
|
6542
|
+
var JS_EXPRESSION_TYPE = "jsExpression";
|
|
6543
|
+
function undefinedSafe(ref2, k) {
|
|
6544
|
+
return ref2.guarded.has(k + 1);
|
|
6545
|
+
}
|
|
6546
|
+
function stripJsComments(source) {
|
|
6547
|
+
let out = "";
|
|
6548
|
+
let i2 = 0;
|
|
6549
|
+
const n = source.length;
|
|
6550
|
+
while (i2 < n) {
|
|
6551
|
+
const c = source[i2];
|
|
6552
|
+
const next = source[i2 + 1];
|
|
6553
|
+
if (c === "/" && next === "/") {
|
|
6554
|
+
while (i2 < n && source[i2] !== `
|
|
6555
|
+
`)
|
|
6556
|
+
i2++;
|
|
7496
6557
|
continue;
|
|
7497
|
-
|
|
7498
|
-
if (
|
|
7499
|
-
|
|
6558
|
+
}
|
|
6559
|
+
if (c === "/" && next === "*") {
|
|
6560
|
+
const end = source.indexOf("*/", i2 + 2);
|
|
6561
|
+
i2 = end === -1 ? n : end + 2;
|
|
6562
|
+
out += " ";
|
|
7500
6563
|
continue;
|
|
7501
6564
|
}
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
for (const entry of defModel.context) {
|
|
7512
|
-
if (entry.name === "name" && typeof entry.value === "string" && UNRESOLVED_BINDING_PATTERN.test(entry.value)) {
|
|
7513
|
-
entry.default = storedName.default;
|
|
7514
|
-
entry.value = `=bindings.${storedName.id}`;
|
|
7515
|
-
} else if (entry.name === "folderPath" && typeof entry.value === "string" && UNRESOLVED_BINDING_PATTERN.test(entry.value)) {
|
|
7516
|
-
entry.default = storedFolder.default;
|
|
7517
|
-
entry.value = `=bindings.${storedFolder.id}`;
|
|
7518
|
-
}
|
|
6565
|
+
if (c === "'" || c === '"' || c === "`") {
|
|
6566
|
+
let j = i2 + 1;
|
|
6567
|
+
while (j < n && source[j] !== c) {
|
|
6568
|
+
if (source[j] === "\\")
|
|
6569
|
+
j++;
|
|
6570
|
+
else if (c !== "`" && source[j] === `
|
|
6571
|
+
`)
|
|
6572
|
+
break;
|
|
6573
|
+
j++;
|
|
7519
6574
|
}
|
|
6575
|
+
out += source.slice(i2, Math.min(j + 1, n));
|
|
6576
|
+
i2 = j + 1;
|
|
6577
|
+
continue;
|
|
7520
6578
|
}
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
logger.info(`Resolved process bindings for node "${node.id}" (${node.type})`);
|
|
7524
|
-
}
|
|
7525
|
-
if (bindingsCreated > 0) {
|
|
7526
|
-
logger.info(`ensureProcessBindings: created ${bindingsCreated} binding(s) for directly-authored nodes`);
|
|
6579
|
+
out += c;
|
|
6580
|
+
i2++;
|
|
7527
6581
|
}
|
|
7528
|
-
return
|
|
6582
|
+
return out;
|
|
7529
6583
|
}
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
6584
|
+
function isExpressionObject(v) {
|
|
6585
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) && typeof v.expression === "string" && typeof v.type === "string";
|
|
6586
|
+
}
|
|
6587
|
+
function parseRefs(text) {
|
|
6588
|
+
const refs = [];
|
|
6589
|
+
for (const m of text.matchAll(VARS_REF_RE)) {
|
|
6590
|
+
const chain = m[1];
|
|
6591
|
+
const segments = [];
|
|
6592
|
+
const guarded = new Set;
|
|
6593
|
+
for (const step of chain.matchAll(VARS_STEP_RE)) {
|
|
6594
|
+
if (step[1] === "?.")
|
|
6595
|
+
guarded.add(segments.length);
|
|
6596
|
+
const key = step[2] ?? step[3] ?? step[4] ?? "";
|
|
6597
|
+
for (const part of key.split(".")) {
|
|
6598
|
+
if (part)
|
|
6599
|
+
segments.push(part);
|
|
6600
|
+
}
|
|
6601
|
+
}
|
|
6602
|
+
if (segments.length > 0) {
|
|
6603
|
+
refs.push({
|
|
6604
|
+
segments,
|
|
6605
|
+
text: `$vars${chain.replace(/\s+/g, "")}`,
|
|
6606
|
+
guarded
|
|
6607
|
+
});
|
|
7540
6608
|
}
|
|
7541
6609
|
}
|
|
7542
6610
|
return refs;
|
|
7543
6611
|
}
|
|
7544
|
-
function
|
|
7545
|
-
const
|
|
7546
|
-
const
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
6612
|
+
function scanNodeFields(node, updates) {
|
|
6613
|
+
const out = [];
|
|
6614
|
+
const walk = (val, path, underAgentBinding) => {
|
|
6615
|
+
const isScriptBody = node.type === SCRIPT_NODE_TYPE && path === "inputs.script";
|
|
6616
|
+
if (typeof val === "string") {
|
|
6617
|
+
if (!val.includes("$vars"))
|
|
6618
|
+
return;
|
|
6619
|
+
out.push({
|
|
6620
|
+
fieldPath: path,
|
|
6621
|
+
isAgentBinding: underAgentBinding,
|
|
6622
|
+
isJs: isScriptBody || val.startsWith(JS_EXPRESSION_PREFIX),
|
|
6623
|
+
text: val
|
|
6624
|
+
});
|
|
6625
|
+
return;
|
|
6626
|
+
}
|
|
6627
|
+
if (isExpressionObject(val)) {
|
|
6628
|
+
if (!val.expression.includes("$vars"))
|
|
6629
|
+
return;
|
|
6630
|
+
out.push({
|
|
6631
|
+
fieldPath: `${path}.expression`,
|
|
6632
|
+
isAgentBinding: underAgentBinding,
|
|
6633
|
+
isJs: isScriptBody || val.type === JS_EXPRESSION_TYPE,
|
|
6634
|
+
text: val.expression
|
|
6635
|
+
});
|
|
6636
|
+
return;
|
|
6637
|
+
}
|
|
6638
|
+
if (Array.isArray(val)) {
|
|
6639
|
+
for (const [i2, v] of val.entries()) {
|
|
6640
|
+
walk(v, `${path}[${i2}]`, underAgentBinding);
|
|
6641
|
+
}
|
|
6642
|
+
return;
|
|
6643
|
+
}
|
|
6644
|
+
if (val && typeof val === "object") {
|
|
6645
|
+
for (const [k, v] of Object.entries(val)) {
|
|
6646
|
+
if (path === "inputs.detail" && k === "configuration")
|
|
6647
|
+
continue;
|
|
6648
|
+
walk(v, `${path}.${k}`, underAgentBinding || path === "inputs" && k === "agentInputVariables");
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
};
|
|
6652
|
+
if (node.inputs)
|
|
6653
|
+
walk(node.inputs, "inputs", false);
|
|
6654
|
+
if (node.outputs)
|
|
6655
|
+
walk(node.outputs, "outputs", false);
|
|
6656
|
+
if (Array.isArray(updates)) {
|
|
6657
|
+
for (const [i2, u] of updates.entries()) {
|
|
6658
|
+
const expr = u?.expression;
|
|
6659
|
+
if (typeof expr === "string" || isExpressionObject(expr)) {
|
|
6660
|
+
walk(expr, `variableUpdates[${i2}]`, false);
|
|
6661
|
+
}
|
|
7558
6662
|
}
|
|
7559
6663
|
}
|
|
7560
|
-
return
|
|
7561
|
-
}
|
|
7562
|
-
var addNodeToFlow = async (filePath, nodeType, options = {}) => {
|
|
7563
|
-
const [registryErr, registryManifest] = await catchError(getNode(nodeType));
|
|
7564
|
-
let manifest = registryManifest ?? null;
|
|
7565
|
-
if (registryErr) {
|
|
7566
|
-
logger.debug(`Registry node lookup failed for ${nodeType}: ${registryErr.message}`);
|
|
7567
|
-
}
|
|
7568
|
-
let localErr = null;
|
|
7569
|
-
if (!manifest) {
|
|
7570
|
-
const [err, localManifest] = await catchError(getNode(nodeType, { local: true }));
|
|
7571
|
-
manifest = localManifest ?? null;
|
|
7572
|
-
if (err) {
|
|
7573
|
-
localErr = err;
|
|
7574
|
-
logger.warn(`In-solution node lookup failed for ${nodeType}: ${err.message}`);
|
|
7575
|
-
}
|
|
7576
|
-
}
|
|
7577
|
-
if (!manifest) {
|
|
7578
|
-
if (isKnownSdkNodeType(nodeType)) {
|
|
7579
|
-
throw new NodeTypeNotEnabledError(`Node type '${nodeType}' is not enabled for this tenant.`, "Ask an admin to enable the feature flag for this node type, " + "or run `uip maestro flow registry list` to see every " + "known type with its `AvailableOnTenant` flag.");
|
|
7580
|
-
}
|
|
7581
|
-
const details = [
|
|
7582
|
-
registryErr && `Registry lookup failed: ${registryErr.message}`,
|
|
7583
|
-
localErr && `In-solution lookup failed: ${localErr.message}`
|
|
7584
|
-
].filter(Boolean).join(". ");
|
|
7585
|
-
const hint = details || "If this is an in-solution node, make sure you are running " + "from inside a solution directory (with a .uipx file in CWD, parent, or grandparent).";
|
|
7586
|
-
throw new NodeTypeNotFoundError(`Node type not found: ${nodeType}. ${hint}`, "Run `uip maestro flow registry search <keyword>` to find the " + "correct node type, or `uip maestro flow registry list` " + "to see every known type (including ones not enabled on this tenant).");
|
|
7587
|
-
}
|
|
7588
|
-
if (manifest.model?.source === true) {
|
|
7589
|
-
const fromFlag = typeof options.source === "string" && options.source.length > 0;
|
|
7590
|
-
const inputsSourceCandidate = options.inputs?.source;
|
|
7591
|
-
const fromInputs = typeof inputsSourceCandidate === "string" && inputsSourceCandidate.length > 0;
|
|
7592
|
-
if (!fromFlag && !fromInputs) {
|
|
7593
|
-
throw new SourceRequiredError(`Node type "${nodeType}" requires a source UUID at inputs.source. Pass --source <uuid>.`, "Use the agent's ProjectId (returned by " + '`uip agent init "<flow-dir>" --inline-in-flow`) for inline ' + "agent nodes, or the resource id declared in the parent " + "inline agent's `resources[]` for agent-tool resource nodes. " + '`--input \'{"source":"<uuid>"}\'` is also accepted.');
|
|
7594
|
-
}
|
|
7595
|
-
}
|
|
7596
|
-
const { workflow, refMap } = await readFlowWorkflow(filePath);
|
|
7597
|
-
const existingIds = new Set(workflow.nodes.map((n) => n.id));
|
|
7598
|
-
const existingDefCount = workflow.definitions.length;
|
|
7599
|
-
const { id, displayName } = generateNodeIdAndName(options.label ?? manifest.display.label, existingIds);
|
|
7600
|
-
const bindingsBefore = workflow.bindings?.length ?? 0;
|
|
7601
|
-
const node = addNode(workflow, manifest, id, options.position ?? { x: 0, y: 0 }, options.label ?? displayName);
|
|
7602
|
-
const schema = manifest.inputDefinition;
|
|
7603
|
-
if (manifest.inputDefaults === undefined && schema?.type === "object") {
|
|
7604
|
-
for (const schemaKey of Object.keys(schema)) {
|
|
7605
|
-
delete node.inputs[schemaKey];
|
|
7606
|
-
}
|
|
7607
|
-
}
|
|
7608
|
-
let bindingsCreated = 0;
|
|
7609
|
-
if (isProcessNode(manifest)) {
|
|
7610
|
-
const { nameBinding, folderBinding } = createProcessBindings(manifest);
|
|
7611
|
-
addBinding(workflow, nameBinding);
|
|
7612
|
-
addBinding(workflow, folderBinding);
|
|
7613
|
-
const storedBindings = workflow.bindings;
|
|
7614
|
-
const storedName = storedBindings.find((b) => b.resourceKey === nameBinding.resourceKey && b.propertyAttribute === nameBinding.propertyAttribute) ?? nameBinding;
|
|
7615
|
-
const storedFolder = storedBindings.find((b) => b.resourceKey === folderBinding.resourceKey && b.propertyAttribute === folderBinding.propertyAttribute) ?? folderBinding;
|
|
7616
|
-
if (folderBinding.default === "" && storedFolder.default === undefined) {
|
|
7617
|
-
storedFolder.default = "";
|
|
7618
|
-
}
|
|
7619
|
-
resolveProcessBindingsInContext(node, storedName, storedFolder);
|
|
7620
|
-
bindingsCreated = (workflow.bindings?.length ?? 0) - bindingsBefore;
|
|
7621
|
-
}
|
|
7622
|
-
if (options.inputs && Object.keys(options.inputs).length > 0) {
|
|
7623
|
-
node.inputs = { ...node.inputs ?? {}, ...options.inputs };
|
|
7624
|
-
}
|
|
7625
|
-
const manifestModel = manifest.model;
|
|
7626
|
-
if (manifestModel?.source === true) {
|
|
7627
|
-
if (typeof options.source === "string" && options.source.length > 0) {
|
|
7628
|
-
const inputs = node.inputs ?? {};
|
|
7629
|
-
inputs.source = options.source;
|
|
7630
|
-
node.inputs = inputs;
|
|
7631
|
-
}
|
|
7632
|
-
if (node.model) {
|
|
7633
|
-
delete node.model;
|
|
7634
|
-
}
|
|
7635
|
-
}
|
|
7636
|
-
if (typeof options.parent === "string" && options.parent.length > 0) {
|
|
7637
|
-
const allNodes = workflow.nodes;
|
|
7638
|
-
if (!allNodes.some((n) => n.id === options.parent)) {
|
|
7639
|
-
throw new Error(`--parent value "${options.parent}" is not an existing node id in this flow. ` + "Run 'uip maestro flow node list <file>' to see valid ids.");
|
|
7640
|
-
}
|
|
7641
|
-
node.parentId = options.parent;
|
|
7642
|
-
}
|
|
7643
|
-
await writeFlowWorkflow(filePath, workflow, refMap);
|
|
7644
|
-
if (isProcessNode(manifest)) {
|
|
7645
|
-
const fs = getFileSystem();
|
|
7646
|
-
const [regenErr] = await catchError(regenerateBindingsFile(fs, filePath));
|
|
7647
|
-
if (regenErr) {
|
|
7648
|
-
logger.warn(`Failed to regenerate bindings_v2.json after adding ${manifest.nodeType}: ${regenErr.message}`);
|
|
7649
|
-
}
|
|
7650
|
-
}
|
|
7651
|
-
const definitionAdded = workflow.definitions.length > existingDefCount;
|
|
7652
|
-
const variableCount = workflow.variables?.nodes?.length ?? 0;
|
|
7653
|
-
if (isConnectorNode(manifest)) {
|
|
7654
|
-
const connInfo = getConnectorInfo(manifest);
|
|
7655
|
-
return {
|
|
7656
|
-
node,
|
|
7657
|
-
definitionAdded,
|
|
7658
|
-
bindingsCreated,
|
|
7659
|
-
variableCount,
|
|
7660
|
-
connectorInfo: connInfo ?? undefined
|
|
7661
|
-
};
|
|
7662
|
-
} else {
|
|
7663
|
-
return { node, definitionAdded, bindingsCreated, variableCount };
|
|
7664
|
-
}
|
|
7665
|
-
};
|
|
7666
|
-
function removeConnectedEdges(workflow, nodeId) {
|
|
7667
|
-
const edges = workflow.edges ?? [];
|
|
7668
|
-
const before = edges.length;
|
|
7669
|
-
workflow.edges = edges.filter((e) => e.sourceNodeId !== nodeId && e.targetNodeId !== nodeId);
|
|
7670
|
-
return before - workflow.edges.length;
|
|
6664
|
+
return out;
|
|
7671
6665
|
}
|
|
7672
|
-
function
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
6666
|
+
function buildAncestors(flow) {
|
|
6667
|
+
const preds = new Map;
|
|
6668
|
+
for (const e of flow.edges) {
|
|
6669
|
+
const set = preds.get(e.targetNodeId) ?? new Set;
|
|
6670
|
+
set.add(e.sourceNodeId);
|
|
6671
|
+
preds.set(e.targetNodeId, set);
|
|
6672
|
+
}
|
|
6673
|
+
const result = new Map;
|
|
6674
|
+
for (const n of flow.nodes) {
|
|
6675
|
+
const seen = new Set;
|
|
6676
|
+
const stack = [...preds.get(n.id) ?? []];
|
|
6677
|
+
while (stack.length > 0) {
|
|
6678
|
+
const x = stack.pop();
|
|
6679
|
+
if (seen.has(x))
|
|
6680
|
+
continue;
|
|
6681
|
+
seen.add(x);
|
|
6682
|
+
for (const p of preds.get(x) ?? [])
|
|
6683
|
+
stack.push(p);
|
|
7680
6684
|
}
|
|
6685
|
+
result.set(n.id, seen);
|
|
7681
6686
|
}
|
|
7682
|
-
|
|
7683
|
-
workflow.bindings = bindings.filter((b) => !deletedNodeKeys.has(b.resourceKey) || remainingKeys.has(b.resourceKey));
|
|
7684
|
-
return before - workflow.bindings.length;
|
|
7685
|
-
}
|
|
7686
|
-
function connectorConnectionBindingName(node, definitions) {
|
|
7687
|
-
if (!node.type.startsWith("uipath.connector"))
|
|
7688
|
-
return "";
|
|
7689
|
-
const def = definitions.find((d) => d.nodeType === node.type);
|
|
7690
|
-
const context = def?.model?.context;
|
|
7691
|
-
const connectorKey = context?.find((c) => c.name === "connectorKey")?.value;
|
|
7692
|
-
return typeof connectorKey === "string" && connectorKey.length > 0 ? `${connectorKey} connection` : "";
|
|
7693
|
-
}
|
|
7694
|
-
function removeOrphanedConnectorStub(workflow, deletedNode, remainingNodes, definitions) {
|
|
7695
|
-
const stubName = connectorConnectionBindingName(deletedNode, definitions);
|
|
7696
|
-
if (!stubName)
|
|
7697
|
-
return 0;
|
|
7698
|
-
const stillOwned = remainingNodes.some((n) => connectorConnectionBindingName(n, definitions) === stubName);
|
|
7699
|
-
if (stillOwned)
|
|
7700
|
-
return 0;
|
|
7701
|
-
const bindings = workflow.bindings ?? [];
|
|
7702
|
-
if (bindings.length === 0)
|
|
7703
|
-
return 0;
|
|
7704
|
-
const before = bindings.length;
|
|
7705
|
-
workflow.bindings = bindings.filter((b) => !(b.resource?.toLowerCase() === "connection" && b.propertyAttribute === "ConnectionId" && !b.resourceKey && !b.default && b.name === stubName));
|
|
7706
|
-
return before - workflow.bindings.length;
|
|
6687
|
+
return result;
|
|
7707
6688
|
}
|
|
7708
|
-
function
|
|
7709
|
-
const
|
|
7710
|
-
const
|
|
7711
|
-
const
|
|
7712
|
-
|
|
7713
|
-
|
|
6689
|
+
function buildMustRun(flow, modelTypeOf) {
|
|
6690
|
+
const nodesById = new Map(flow.nodes.map((n) => [n.id, n]));
|
|
6691
|
+
const incoming = new Map;
|
|
6692
|
+
for (const e of flow.edges) {
|
|
6693
|
+
if (e.targetPort === LOOP_BACK_TARGET_PORT && nodesById.get(e.targetNodeId)?.type === LOOP_NODE_TYPE) {
|
|
6694
|
+
continue;
|
|
6695
|
+
}
|
|
6696
|
+
const list2 = incoming.get(e.targetNodeId) ?? [];
|
|
6697
|
+
list2.push(e.sourceNodeId);
|
|
6698
|
+
incoming.set(e.targetNodeId, list2);
|
|
6699
|
+
}
|
|
6700
|
+
const memo = new Map;
|
|
6701
|
+
const visiting = new Set;
|
|
6702
|
+
const mustRun = (id) => {
|
|
6703
|
+
const hit = memo.get(id);
|
|
6704
|
+
if (hit)
|
|
6705
|
+
return hit;
|
|
6706
|
+
if (visiting.has(id))
|
|
6707
|
+
return new Set;
|
|
6708
|
+
visiting.add(id);
|
|
6709
|
+
const ins = incoming.get(id) ?? [];
|
|
6710
|
+
let acc = new Set;
|
|
6711
|
+
if (ins.length > 0) {
|
|
6712
|
+
const sets = ins.map((s) => new Set([...mustRun(s), s]));
|
|
6713
|
+
const node = nodesById.get(id);
|
|
6714
|
+
const isJoin = node !== undefined && (node.type === "core.logic.merge" || modelTypeOf(node) === "bpmn:ParallelGateway");
|
|
6715
|
+
if (isJoin) {
|
|
6716
|
+
for (const s of sets)
|
|
6717
|
+
for (const x of s)
|
|
6718
|
+
acc.add(x);
|
|
6719
|
+
} else {
|
|
6720
|
+
acc = new Set(sets[0]);
|
|
6721
|
+
for (const s of sets.slice(1)) {
|
|
6722
|
+
for (const x of [...acc])
|
|
6723
|
+
if (!s.has(x))
|
|
6724
|
+
acc.delete(x);
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
acc.add(id);
|
|
6729
|
+
visiting.delete(id);
|
|
6730
|
+
memo.set(id, acc);
|
|
6731
|
+
return acc;
|
|
6732
|
+
};
|
|
6733
|
+
return mustRun;
|
|
6734
|
+
}
|
|
6735
|
+
function declaredOutputs(node, definition) {
|
|
6736
|
+
const names = new Set;
|
|
6737
|
+
if (node.outputs && typeof node.outputs === "object") {
|
|
6738
|
+
for (const k of Object.keys(node.outputs))
|
|
6739
|
+
names.add(k);
|
|
6740
|
+
}
|
|
6741
|
+
const outDef = definition?.outputDefinition;
|
|
6742
|
+
if (outDef && typeof outDef === "object") {
|
|
6743
|
+
for (const k of Object.keys(outDef))
|
|
6744
|
+
names.add(k);
|
|
6745
|
+
}
|
|
6746
|
+
if (node.type === "core.subflow")
|
|
6747
|
+
names.add("output");
|
|
6748
|
+
const isListOutput = node.type.startsWith("uipath.connector.") && connectorOperation(node) === "list";
|
|
6749
|
+
const declaredType = (name) => {
|
|
6750
|
+
const entry = outDef?.[name];
|
|
6751
|
+
const t = entry?.type;
|
|
6752
|
+
return typeof t === "string" ? t : undefined;
|
|
6753
|
+
};
|
|
6754
|
+
const typeOf = (name) => name === "output" && isListOutput ? "array" : declaredType(name);
|
|
6755
|
+
const whyArray = (name) => declaredType(name) === "array" ? 'declared type "array"' : 'a list operation — inputMetadata.operation is "list", the response is the bare array of records';
|
|
6756
|
+
return { names, typeOf, whyArray };
|
|
7714
6757
|
}
|
|
7715
|
-
function
|
|
7716
|
-
const
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
6758
|
+
function connectorOperation(node) {
|
|
6759
|
+
const detail = node.inputs?.detail;
|
|
6760
|
+
const op = detail?.inputMetadata?.operation;
|
|
6761
|
+
return typeof op === "string" ? op.toLowerCase() : undefined;
|
|
6762
|
+
}
|
|
6763
|
+
function boundedEditDistance(a, b, max) {
|
|
6764
|
+
if (Math.abs(a.length - b.length) > max)
|
|
6765
|
+
return max + 1;
|
|
6766
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
6767
|
+
for (let i2 = 1;i2 <= a.length; i2++) {
|
|
6768
|
+
const cur = [i2];
|
|
6769
|
+
let rowMin = i2;
|
|
6770
|
+
for (let j = 1;j <= b.length; j++) {
|
|
6771
|
+
const cost = a[i2 - 1] === b[j - 1] ? 0 : 1;
|
|
6772
|
+
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + cost);
|
|
6773
|
+
rowMin = Math.min(rowMin, cur[j]);
|
|
6774
|
+
}
|
|
6775
|
+
if (rowMin > max)
|
|
6776
|
+
return max + 1;
|
|
6777
|
+
prev = cur;
|
|
6778
|
+
}
|
|
6779
|
+
return prev[b.length];
|
|
6780
|
+
}
|
|
6781
|
+
var HINT_MAX_EDITS = 2;
|
|
6782
|
+
var HINT_MIN_PREFIX = 3;
|
|
6783
|
+
function nearestNodeId(root, ids) {
|
|
6784
|
+
const lower = root.toLowerCase();
|
|
6785
|
+
let best;
|
|
6786
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
6787
|
+
for (const id of ids) {
|
|
6788
|
+
const l = id.toLowerCase();
|
|
6789
|
+
if (l === lower)
|
|
6790
|
+
return id;
|
|
6791
|
+
if (lower.length < HINT_MIN_PREFIX)
|
|
6792
|
+
continue;
|
|
6793
|
+
let score = boundedEditDistance(lower, l, HINT_MAX_EDITS);
|
|
6794
|
+
if (score > HINT_MAX_EDITS) {
|
|
6795
|
+
const [short, long] = lower.length <= l.length ? [lower, l] : [l, lower];
|
|
6796
|
+
const isPrefix = short.length >= HINT_MIN_PREFIX && short.length * 2 >= long.length && long.startsWith(short);
|
|
6797
|
+
if (!isPrefix)
|
|
6798
|
+
continue;
|
|
6799
|
+
score = HINT_MAX_EDITS + (long.length - short.length);
|
|
6800
|
+
}
|
|
6801
|
+
if (score < bestScore) {
|
|
6802
|
+
best = id;
|
|
6803
|
+
bestScore = score;
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
return best;
|
|
7725
6807
|
}
|
|
7726
|
-
function
|
|
7727
|
-
|
|
7728
|
-
if (!variables?.nodes)
|
|
7729
|
-
return 0;
|
|
7730
|
-
const before = variables.nodes.length;
|
|
7731
|
-
variables.nodes = variables.nodes.filter((v) => v.binding.nodeId !== nodeId);
|
|
7732
|
-
return before - variables.nodes.length;
|
|
6808
|
+
function labelOf(node) {
|
|
6809
|
+
return node.display?.label ?? node.id;
|
|
7733
6810
|
}
|
|
7734
|
-
|
|
7735
|
-
const {
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
}
|
|
7742
|
-
const targetNode = nodes[nodeIndex];
|
|
7743
|
-
const deletedWasProcessNode = isProcessNode({ nodeType: targetNode.type });
|
|
7744
|
-
const deletedNode = {
|
|
7745
|
-
id: targetNode.id,
|
|
7746
|
-
type: targetNode.type,
|
|
7747
|
-
label: targetNode.display.label ?? "",
|
|
7748
|
-
position: targetNode.ui.position
|
|
7749
|
-
};
|
|
7750
|
-
const bindings = workflow.bindings ?? [];
|
|
7751
|
-
const definitions = workflow.definitions ?? [];
|
|
7752
|
-
const deletedNodeResourceKeys = collectNodeBindingResourceKeys(targetNode, bindings, definitions);
|
|
7753
|
-
nodes.splice(nodeIndex, 1);
|
|
7754
|
-
const edgesRemoved = removeConnectedEdges(workflow, nodeId);
|
|
7755
|
-
const bindingsRemoved = removeOrphanedBindings(workflow, deletedNodeResourceKeys, nodes, bindings) + removeOrphanedConnectorStub(workflow, targetNode, nodes, definitions);
|
|
7756
|
-
const definitionsRemoved = removeOrphanedDefinitions(workflow, nodes);
|
|
7757
|
-
const variablesRemoved = removeNodeVariables(workflow, nodeId);
|
|
7758
|
-
const variableUpdatesRemoved = removeVariableUpdates(workflow, nodeId);
|
|
7759
|
-
await writeFlowWorkflow(filePath, workflow, refMap);
|
|
7760
|
-
if (deletedWasProcessNode || bindingsRemoved > 0) {
|
|
7761
|
-
const fs = getFileSystem();
|
|
7762
|
-
const [regenErr] = await catchError(regenerateBindingsFile(fs, filePath));
|
|
7763
|
-
if (regenErr) {
|
|
7764
|
-
logger.warn(`Failed to regenerate bindings_v2.json after deleting ${targetNode.type}: ${regenErr.message}`);
|
|
7765
|
-
}
|
|
6811
|
+
function arrayMemberIssue(args) {
|
|
6812
|
+
const { names, typeOf, whyArray } = declaredOutputs(args.producer, args.definition);
|
|
6813
|
+
if (!names.has(args.second) || typeOf(args.second) !== "array") {
|
|
6814
|
+
return;
|
|
6815
|
+
}
|
|
6816
|
+
if (/^\d+$/.test(args.third) || ARRAY_MEMBERS.has(args.third)) {
|
|
6817
|
+
return;
|
|
7766
6818
|
}
|
|
6819
|
+
const { root, second, third } = args;
|
|
7767
6820
|
return {
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
definitionsRemoved,
|
|
7772
|
-
variablesRemoved,
|
|
7773
|
-
variableUpdatesRemoved
|
|
6821
|
+
path: args.path,
|
|
6822
|
+
severity: args.undefinedSafe || args.embedded ? "warning" : args.severity,
|
|
6823
|
+
message: `"${args.reader}" reads "${args.refText}", but "$vars.${root}.${second}" is an array (${whyArray(second)}), so ".${third}" is undefined. ` + `Iterate it (a Loop's collection is "$vars.${root}.${second}" itself), index it ("[0].${third}"), or use an Array method.`
|
|
7774
6824
|
};
|
|
7775
|
-
}
|
|
6825
|
+
}
|
|
6826
|
+
function validateVarsReferences(flow, options = {}) {
|
|
6827
|
+
const issues = [];
|
|
6828
|
+
const nodesById = new Map(flow.nodes.map((n) => [n.id, n]));
|
|
6829
|
+
if (nodesById.size === 0)
|
|
6830
|
+
return issues;
|
|
6831
|
+
const knownRoots = new Set(options.extraKnownRoots ?? []);
|
|
6832
|
+
const variables = flow.variables;
|
|
6833
|
+
for (const g of variables?.globals ?? []) {
|
|
6834
|
+
if (typeof g?.id === "string")
|
|
6835
|
+
knownRoots.add(g.id);
|
|
6836
|
+
}
|
|
6837
|
+
const updatesByNode = variables?.variableUpdates ?? {};
|
|
6838
|
+
const defByKey = new Map;
|
|
6839
|
+
const defByType = new Map;
|
|
6840
|
+
for (const d of flow.definitions) {
|
|
6841
|
+
defByKey.set(`${d.nodeType}:${d.version}`, d);
|
|
6842
|
+
if (!defByType.has(d.nodeType))
|
|
6843
|
+
defByType.set(d.nodeType, d);
|
|
6844
|
+
}
|
|
6845
|
+
const definitionOf = (n) => (n.typeVersion ? defByKey.get(`${n.type}:${n.typeVersion}`) : undefined) ?? defByType.get(n.type);
|
|
6846
|
+
const modelTypeOf = (n) => typeof n.model?.type === "string" ? n.model.type : definitionOf(n)?.model?.type;
|
|
6847
|
+
const ancestors = buildAncestors(flow);
|
|
6848
|
+
const mustRun = buildMustRun(flow, modelTypeOf);
|
|
6849
|
+
for (const node of flow.nodes) {
|
|
6850
|
+
const reader = labelOf(node);
|
|
6851
|
+
const reported = new Set;
|
|
6852
|
+
for (const field of scanNodeFields(node, updatesByNode[node.id])) {
|
|
6853
|
+
const path = `nodes[${node.id}].${field.fieldPath}`;
|
|
6854
|
+
const severity = field.fieldPath.startsWith("inputs") ? "error" : "warning";
|
|
6855
|
+
const text = field.isJs ? stripJsComments(field.text) : field.text;
|
|
6856
|
+
const bareValue = text.replace(/^=(js:)?/, "").replace(/\s+/g, "");
|
|
6857
|
+
for (const ref2 of parseRefs(text)) {
|
|
6858
|
+
const embedded = bareValue !== ref2.text;
|
|
6859
|
+
const [root, second, third] = ref2.segments;
|
|
6860
|
+
const key = `${field.fieldPath}|${ref2.segments.join(".")}`;
|
|
6861
|
+
if (reported.has(key))
|
|
6862
|
+
continue;
|
|
6863
|
+
reported.add(key);
|
|
6864
|
+
if (root === node.id)
|
|
6865
|
+
continue;
|
|
6866
|
+
if (knownRoots.has(root))
|
|
6867
|
+
continue;
|
|
6868
|
+
const producer = nodesById.get(root);
|
|
6869
|
+
const anc = ancestors.get(node.id) ?? new Set;
|
|
6870
|
+
if (field.isAgentBinding) {
|
|
6871
|
+
if (producer && anc.size > 0 && anc.has(root)) {
|
|
6872
|
+
if (!mustRun(node.id).has(root)) {
|
|
6873
|
+
issues.push({
|
|
6874
|
+
path,
|
|
6875
|
+
severity: undefinedSafe(ref2, 0) ? "warning" : severity,
|
|
6876
|
+
message: `Agent "${reader}" input binds "${ref2.text}", but "${root}" runs only on some of the paths that reach "${node.id}" (it sits behind a Decision or Switch branch the agent does not depend on). ` + "On the other paths the input is undefined and the agent invocation faults ([400300] Error evaluating expression in activity inputs). " + "Bind a node that runs on every path to the agent, or compute the value on each branch before they rejoin."
|
|
6877
|
+
});
|
|
6878
|
+
} else if (second !== undefined && third !== undefined) {
|
|
6879
|
+
const issue = arrayMemberIssue({
|
|
6880
|
+
producer,
|
|
6881
|
+
definition: definitionOf(producer),
|
|
6882
|
+
root,
|
|
6883
|
+
second,
|
|
6884
|
+
third,
|
|
6885
|
+
reader,
|
|
6886
|
+
refText: ref2.text,
|
|
6887
|
+
path,
|
|
6888
|
+
undefinedSafe: undefinedSafe(ref2, 2),
|
|
6889
|
+
embedded,
|
|
6890
|
+
severity
|
|
6891
|
+
});
|
|
6892
|
+
if (issue)
|
|
6893
|
+
issues.push(issue);
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
continue;
|
|
6897
|
+
}
|
|
6898
|
+
if (!producer) {
|
|
6899
|
+
const hint = nearestNodeId(root, nodesById.keys());
|
|
6900
|
+
issues.push({
|
|
6901
|
+
path,
|
|
6902
|
+
severity: undefinedSafe(ref2, 0) ? "warning" : severity,
|
|
6903
|
+
message: `"${reader}" reads "${ref2.text}", but no node or workflow variable named "${root}" exists in this flow` + (hint ? ` (did you mean node "${hint}"?)` : "") + ". At runtime the reference resolves to nothing — a Script reading it faults ([300501]), a Decision or Switch faults ([400302]), a Transform or connector input silently gets an empty value. Fix the id or declare the variable under variables.globals."
|
|
6904
|
+
});
|
|
6905
|
+
continue;
|
|
6906
|
+
}
|
|
6907
|
+
if (anc.size > 0 && !anc.has(root)) {
|
|
6908
|
+
const runsAfter = (ancestors.get(root) ?? new Set).has(node.id);
|
|
6909
|
+
const why = runsAfter ? `"${root}" runs AFTER "${node.id}" (it is downstream of it)` : `"${root}" is not on any path that reaches "${node.id}" (another branch, or not connected)`;
|
|
6910
|
+
issues.push({
|
|
6911
|
+
path,
|
|
6912
|
+
severity: undefinedSafe(ref2, 0) ? "warning" : severity,
|
|
6913
|
+
message: `"${reader}" reads "${ref2.text}", but ${why}, so "${root}" has not produced a value when this expression is evaluated ` + "(Decision/Switch: [400302]; Script: [300501]; agent or connector input: [400300]/empty value). " + `Reference a node that runs earlier on this path, or move "${node.id}" after "${root}".`
|
|
6914
|
+
});
|
|
6915
|
+
continue;
|
|
6916
|
+
}
|
|
6917
|
+
if (second !== undefined) {
|
|
6918
|
+
const { names } = declaredOutputs(producer, definitionOf(producer));
|
|
6919
|
+
const declaresOutputs = [...names].some((n) => n !== "error");
|
|
6920
|
+
if (declaresOutputs && !names.has(second)) {
|
|
6921
|
+
const available = [...names].sort((a, b) => a.localeCompare(b)).join(", ");
|
|
6922
|
+
issues.push({
|
|
6923
|
+
path,
|
|
6924
|
+
severity: undefinedSafe(ref2, 1) ? "warning" : severity,
|
|
6925
|
+
message: `"${reader}" reads "${ref2.text}", but node "${root}" has no output named "${second}" — its outputs are: ${available}. ` + `Read "$vars.${root}.<one of those>" (e.g. a Decision exposes "$vars.${root}.matchedCaseId", not ".output.matchedCaseId").`
|
|
6926
|
+
});
|
|
6927
|
+
continue;
|
|
6928
|
+
}
|
|
6929
|
+
if (third !== undefined) {
|
|
6930
|
+
const issue = arrayMemberIssue({
|
|
6931
|
+
producer,
|
|
6932
|
+
definition: definitionOf(producer),
|
|
6933
|
+
root,
|
|
6934
|
+
second,
|
|
6935
|
+
third,
|
|
6936
|
+
reader,
|
|
6937
|
+
refText: ref2.text,
|
|
6938
|
+
path,
|
|
6939
|
+
undefinedSafe: undefinedSafe(ref2, 2),
|
|
6940
|
+
embedded,
|
|
6941
|
+
severity
|
|
6942
|
+
});
|
|
6943
|
+
if (issue)
|
|
6944
|
+
issues.push(issue);
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6948
|
+
}
|
|
6949
|
+
}
|
|
6950
|
+
return issues;
|
|
6951
|
+
}
|
|
7776
6952
|
|
|
7777
6953
|
// src/services/flow-validate-service.ts
|
|
7778
6954
|
if (!instance.isInitialized) {
|
|
@@ -7947,19 +7123,7 @@ function validationErrorToIssue(error) {
|
|
|
7947
7123
|
};
|
|
7948
7124
|
}
|
|
7949
7125
|
function buildValidationContext(flow) {
|
|
7950
|
-
const nodes = flow.nodes
|
|
7951
|
-
id: n.id,
|
|
7952
|
-
type: n.type,
|
|
7953
|
-
data: {
|
|
7954
|
-
inputs: n.inputs ?? {},
|
|
7955
|
-
outputs: n.outputs ?? {},
|
|
7956
|
-
...n.typeVersion !== undefined && { typeVersion: n.typeVersion },
|
|
7957
|
-
...n.display !== undefined && {
|
|
7958
|
-
display: n.display,
|
|
7959
|
-
label: n.display.label
|
|
7960
|
-
}
|
|
7961
|
-
}
|
|
7962
|
-
}));
|
|
7126
|
+
const nodes = toSerializedNodes(flow.nodes);
|
|
7963
7127
|
const edges = flow.edges.map((e) => ({
|
|
7964
7128
|
id: e.id,
|
|
7965
7129
|
source: e.sourceNodeId,
|
|
@@ -8026,6 +7190,7 @@ class FlowValidateService {
|
|
|
8026
7190
|
validateCurrentManifests;
|
|
8027
7191
|
currentManifestOverrides;
|
|
8028
7192
|
strictBindings;
|
|
7193
|
+
strictExpressions;
|
|
8029
7194
|
connectorCatalogOverride;
|
|
8030
7195
|
currentManifestCache;
|
|
8031
7196
|
connectorCatalogCache;
|
|
@@ -8041,6 +7206,7 @@ class FlowValidateService {
|
|
|
8041
7206
|
this.validateCurrentManifests = options.validateCurrentManifests === true || options.currentManifests !== undefined;
|
|
8042
7207
|
this.currentManifestOverrides = options.currentManifests;
|
|
8043
7208
|
this.strictBindings = options.strictBindings === true;
|
|
7209
|
+
this.strictExpressions = options.strictExpressions === true;
|
|
8044
7210
|
this.connectorCatalogOverride = options.connectorCatalog;
|
|
8045
7211
|
}
|
|
8046
7212
|
async execute(flowFilePath) {
|
|
@@ -8118,13 +7284,13 @@ ${warnings.map((i2) => ` - [${i2.path}] ${i2.message}`).join(`
|
|
|
8118
7284
|
]));
|
|
8119
7285
|
}
|
|
8120
7286
|
async validateFile(flowFilePath) {
|
|
8121
|
-
|
|
7287
|
+
let absolutePath = this.fs.path.resolve(flowFilePath);
|
|
8122
7288
|
if (!await this.fs.exists(absolutePath)) {
|
|
8123
7289
|
throw new Error(`File not found: ${absolutePath}`);
|
|
8124
7290
|
}
|
|
8125
7291
|
const stats = await this.fs.stat(absolutePath);
|
|
8126
7292
|
if (stats?.isDirectory()) {
|
|
8127
|
-
|
|
7293
|
+
absolutePath = await resolveProjectFlowFile(absolutePath);
|
|
8128
7294
|
}
|
|
8129
7295
|
if (!absolutePath.endsWith(".flow")) {
|
|
8130
7296
|
throw new Error(`Expected a .flow file but got: ${this.fs.path.basename(absolutePath)}`);
|
|
@@ -8176,6 +7342,7 @@ Validating: ${absolutePath}
|
|
|
8176
7342
|
issues.push(...this.validateAgentInputScopeRefs(workflow, hydrationDiagnoses, ""));
|
|
8177
7343
|
issues.push(...this.validateVoiceAgentSettings(workflow, hydrationDiagnoses, ""));
|
|
8178
7344
|
issues.push(...await this.validateSolutionRegistrationAndLayout(absolutePath));
|
|
7345
|
+
issues.push(...await validateExpressions(workflow, "", this.strictExpressions));
|
|
8179
7346
|
const subflows = workflow.subflows ?? {};
|
|
8180
7347
|
for (const [subflowId, entry] of Object.entries(subflows)) {
|
|
8181
7348
|
const subNodes = entry.nodes;
|
|
@@ -8189,7 +7356,7 @@ Validating: ${absolutePath}
|
|
|
8189
7356
|
variables: entry.variables
|
|
8190
7357
|
};
|
|
8191
7358
|
const prefix = `subflows[${subflowId}].`;
|
|
8192
|
-
issues.push(...this.validateGraph(subGraph, prefix, rawDefErrorHandling, currentManifests));
|
|
7359
|
+
issues.push(...this.validateGraph(subGraph, prefix, rawDefErrorHandling, currentManifests, (workflow.variables?.globals ?? []).map((g) => g.id).filter((id) => typeof id === "string")));
|
|
8193
7360
|
issues.push(...this.validateGlobalVariableUniqueness(subGraph, prefix));
|
|
8194
7361
|
issues.push(...this.validateGlobalTriggerBindings(subGraph, prefix));
|
|
8195
7362
|
issues.push(...this.validateUnboundInputGlobals(subGraph, prefix));
|
|
@@ -8197,6 +7364,10 @@ Validating: ${absolutePath}
|
|
|
8197
7364
|
issues.push(...this.validateAgentPromptInputRefs(subGraph, hydrationDiagnoses, prefix));
|
|
8198
7365
|
issues.push(...this.validateAgentInputScopeRefs(subGraph, hydrationDiagnoses, prefix));
|
|
8199
7366
|
issues.push(...this.rejectSubflowVoiceAgents(subGraph, prefix));
|
|
7367
|
+
issues.push(...await validateExpressions({
|
|
7368
|
+
...subGraph,
|
|
7369
|
+
subflows
|
|
7370
|
+
}, prefix, this.strictExpressions));
|
|
8200
7371
|
}
|
|
8201
7372
|
this.enrichInlineAgentPromptIssues(issues, hydrationDiagnoses, flowDir);
|
|
8202
7373
|
return {
|
|
@@ -8352,6 +7523,12 @@ Validating: ${absolutePath}
|
|
|
8352
7523
|
const props = inputSchema.properties;
|
|
8353
7524
|
if (props && typeof props === "object") {
|
|
8354
7525
|
diag.declaredInputKeys = Object.keys(props);
|
|
7526
|
+
const leaves = collectAgentInputLeaves(inputSchema);
|
|
7527
|
+
diag.declaredInputLeaves = leaves;
|
|
7528
|
+
diag.declaredInputPaths = new Set([
|
|
7529
|
+
...leaves.map((leaf) => leaf.path),
|
|
7530
|
+
...diag.declaredInputKeys.map(canonicalAgentInputPath)
|
|
7531
|
+
]);
|
|
8355
7532
|
}
|
|
8356
7533
|
}
|
|
8357
7534
|
}
|
|
@@ -8397,9 +7574,10 @@ Validating: ${absolutePath}
|
|
|
8397
7574
|
if (!byField)
|
|
8398
7575
|
continue;
|
|
8399
7576
|
const declared = new Set(diag.declaredInputKeys ?? []);
|
|
7577
|
+
const declaredPaths = diag.declaredInputPaths ?? new Set;
|
|
8400
7578
|
const label = node.display?.label ?? node.id;
|
|
8401
7579
|
for (const [field, vars] of Object.entries(byField)) {
|
|
8402
|
-
issues.push(...this.buildPromptInputRefIssues(node, field, vars ?? [], declared, label, pathPrefix));
|
|
7580
|
+
issues.push(...this.buildPromptInputRefIssues(node, field, vars ?? [], declared, declaredPaths, label, pathPrefix));
|
|
8403
7581
|
}
|
|
8404
7582
|
}
|
|
8405
7583
|
return issues;
|
|
@@ -8461,7 +7639,7 @@ Validating: ${absolutePath}
|
|
|
8461
7639
|
}
|
|
8462
7640
|
return issues;
|
|
8463
7641
|
}
|
|
8464
|
-
buildPromptInputRefIssues(node, field, vars, declared, label, pathPrefix) {
|
|
7642
|
+
buildPromptInputRefIssues(node, field, vars, declared, declaredPaths, label, pathPrefix) {
|
|
8465
7643
|
const issues = [];
|
|
8466
7644
|
const malformed = [];
|
|
8467
7645
|
const undeclared = [];
|
|
@@ -8469,7 +7647,7 @@ Validating: ${absolutePath}
|
|
|
8469
7647
|
const key = this.canonicalInputKey(raw);
|
|
8470
7648
|
if (key === undefined) {
|
|
8471
7649
|
malformed.push(raw);
|
|
8472
|
-
} else if (!declared.has(key)) {
|
|
7650
|
+
} else if (!declared.has(key) && !declaredPaths.has(canonicalAgentInputPath(key))) {
|
|
8473
7651
|
undeclared.push(key);
|
|
8474
7652
|
}
|
|
8475
7653
|
}
|
|
@@ -8539,8 +7717,8 @@ Validating: ${absolutePath}
|
|
|
8539
7717
|
if (notUpstream.length > 0) {
|
|
8540
7718
|
issues.push({
|
|
8541
7719
|
path,
|
|
8542
|
-
message: `Agent "${label}" reads ${notUpstream.join(", ")}, but no edge path leads from that node to "${node.id}", so it has not run when the agent does and its output is not written yet. Studio Web reports the same variable as not available in this scope. Wire the node upstream of the agent, or bind an input the agent can see.`,
|
|
8543
|
-
severity: "
|
|
7720
|
+
message: `Agent "${label}" reads ${notUpstream.join(", ")}, but no edge path leads from that node to "${node.id}", so it has not run when the agent does and its output is not written yet ([400300] Error evaluating expression in activity inputs). Studio Web reports the same variable as not available in this scope. Wire the node upstream of the agent, or bind an input the agent can see.`,
|
|
7721
|
+
severity: "error"
|
|
8544
7722
|
});
|
|
8545
7723
|
}
|
|
8546
7724
|
const invalidFields = [];
|
|
@@ -8585,10 +7763,10 @@ Validating: ${absolutePath}
|
|
|
8585
7763
|
}
|
|
8586
7764
|
}
|
|
8587
7765
|
};
|
|
8588
|
-
for (const
|
|
8589
|
-
const ref2 = decodeSchemaKeyNodeRef(
|
|
7766
|
+
for (const leaf of diag?.declaredInputLeaves ?? []) {
|
|
7767
|
+
const ref2 = decodeSchemaKeyNodeRef(leaf.flatKey);
|
|
8590
7768
|
if (ref2) {
|
|
8591
|
-
add(ref2.nodeId, ref2.field, `\`$vars.${
|
|
7769
|
+
add(ref2.nodeId, ref2.field, `\`$vars.${leaf.path}\` (agent.json input "${leaf.flatKey}")`);
|
|
8592
7770
|
}
|
|
8593
7771
|
}
|
|
8594
7772
|
const bound = node.inputs?.agentInputVariables;
|
|
@@ -8648,11 +7826,13 @@ Validating: ${absolutePath}
|
|
|
8648
7826
|
Hint: ${hint}`;
|
|
8649
7827
|
}
|
|
8650
7828
|
}
|
|
8651
|
-
validateGraph(graph, pathPrefix, rawDefErrorHandling = new Map, currentManifests = new Map) {
|
|
7829
|
+
validateGraph(graph, pathPrefix, rawDefErrorHandling = new Map, currentManifests = new Map, parentVariableIds = []) {
|
|
8652
7830
|
const issues = [];
|
|
8653
7831
|
issues.push(...this.validateEdgeReferences(graph));
|
|
8654
7832
|
issues.push(...this.validateNodeTypeDefinitions(graph));
|
|
8655
|
-
issues.push(...this.validateUniqueIds(graph))
|
|
7833
|
+
issues.push(...this.validateUniqueIds(graph), ...this.validateExclusiveBranchJoins(graph), ...validateVarsReferences(graph, {
|
|
7834
|
+
extraKnownRoots: parentVariableIds
|
|
7835
|
+
}));
|
|
8656
7836
|
issues.push(...this.validateEdgeHandles(graph, rawDefErrorHandling));
|
|
8657
7837
|
issues.push(...this.validateEdgeHandlesAgainstCurrentManifests(graph, currentManifests));
|
|
8658
7838
|
const ctx = buildValidationContext(graph);
|
|
@@ -8662,7 +7842,10 @@ Validating: ${absolutePath}
|
|
|
8662
7842
|
if (this.availableModelNames) {
|
|
8663
7843
|
ctx.availableModelNames = this.availableModelNames;
|
|
8664
7844
|
}
|
|
8665
|
-
const
|
|
7845
|
+
const dropped = new Set(["expression-ref"]);
|
|
7846
|
+
if (pathPrefix)
|
|
7847
|
+
dropped.add("trigger-required");
|
|
7848
|
+
const rules = cliRules.filter((r) => !dropped.has(r.id));
|
|
8666
7849
|
const ruleErrors = runRules([
|
|
8667
7850
|
...rules,
|
|
8668
7851
|
conditionExpressionRule,
|
|
@@ -8672,6 +7855,17 @@ Validating: ${absolutePath}
|
|
|
8672
7855
|
issues.push(...this.runNodeValidators(graph));
|
|
8673
7856
|
return pathPrefix ? issues.map((i2) => ({ ...i2, path: `${pathPrefix}${i2.path}` })) : issues;
|
|
8674
7857
|
}
|
|
7858
|
+
validateExclusiveBranchJoins(flow) {
|
|
7859
|
+
const graph = indexJoinGraph(flow);
|
|
7860
|
+
const issues = [];
|
|
7861
|
+
for (const node of flow.nodes) {
|
|
7862
|
+
const finding = findUnsatisfiableJoin(node, graph);
|
|
7863
|
+
if (finding) {
|
|
7864
|
+
issues.push(describeUnsatisfiableJoin(node, finding, graph));
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
return issues;
|
|
7868
|
+
}
|
|
8675
7869
|
validateEdgeReferences(flow) {
|
|
8676
7870
|
const issues = [];
|
|
8677
7871
|
const nodeIds = new Set(flow.nodes.map((n) => n.id));
|
|
@@ -8703,6 +7897,9 @@ Validating: ${absolutePath}
|
|
|
8703
7897
|
definedVersionsByType.set(d.nodeType, versions);
|
|
8704
7898
|
}
|
|
8705
7899
|
for (const node of flow.nodes) {
|
|
7900
|
+
if (UI_ONLY_NODE_TYPES.has(node.type)) {
|
|
7901
|
+
continue;
|
|
7902
|
+
}
|
|
8706
7903
|
const key = `${node.type}:${node.typeVersion}`;
|
|
8707
7904
|
if (!definedTypes.has(key)) {
|
|
8708
7905
|
const sameTypeVersions = definedVersionsByType.get(node.type);
|
|
@@ -9177,6 +8374,13 @@ Validating: ${absolutePath}
|
|
|
9177
8374
|
visible: buildErrorHandle().visible
|
|
9178
8375
|
});
|
|
9179
8376
|
}
|
|
8377
|
+
if (entry.source.size > 0 || entry.sourceTemplates.length > 0) {
|
|
8378
|
+
for (const outcomeHandleId of deriveOutcomeHandleIds(node)) {
|
|
8379
|
+
if (!entry.source.has(outcomeHandleId)) {
|
|
8380
|
+
entry.source.set(outcomeHandleId, {});
|
|
8381
|
+
}
|
|
8382
|
+
}
|
|
8383
|
+
}
|
|
9180
8384
|
handlesByNode.set(node.id, entry);
|
|
9181
8385
|
}
|
|
9182
8386
|
const sourceCounts = new Map;
|
|
@@ -9296,7 +8500,78 @@ Validating: ${absolutePath}
|
|
|
9296
8500
|
return issues;
|
|
9297
8501
|
}
|
|
9298
8502
|
}
|
|
8503
|
+
function indexJoinGraph(flow) {
|
|
8504
|
+
const modelTypeByNodeType = new Map;
|
|
8505
|
+
for (const def of flow.definitions) {
|
|
8506
|
+
if (typeof def.model?.type === "string") {
|
|
8507
|
+
modelTypeByNodeType.set(def.nodeType, def.model.type);
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8510
|
+
const incomingByTarget = new Map;
|
|
8511
|
+
for (const edge of flow.edges) {
|
|
8512
|
+
const list2 = incomingByTarget.get(edge.targetNodeId) ?? [];
|
|
8513
|
+
list2.push(edge);
|
|
8514
|
+
incomingByTarget.set(edge.targetNodeId, list2);
|
|
8515
|
+
}
|
|
8516
|
+
return {
|
|
8517
|
+
nodesById: new Map(flow.nodes.map((n) => [n.id, n])),
|
|
8518
|
+
incomingByTarget,
|
|
8519
|
+
modelTypeOf: (node) => typeof node.model?.type === "string" ? node.model.type : modelTypeByNodeType.get(node.type)
|
|
8520
|
+
};
|
|
8521
|
+
}
|
|
8522
|
+
function isParallelJoin(node, graph) {
|
|
8523
|
+
return node.type === "core.logic.merge" || graph.modelTypeOf(node) === "bpmn:ParallelGateway";
|
|
8524
|
+
}
|
|
8525
|
+
function isExclusiveSplit(node, graph) {
|
|
8526
|
+
return node.type === "core.logic.decision" || node.type === "core.logic.switch" || graph.modelTypeOf(node) === "bpmn:ExclusiveGateway";
|
|
8527
|
+
}
|
|
8528
|
+
function walkToExclusiveSplit(edge, graph) {
|
|
8529
|
+
let current2 = edge;
|
|
8530
|
+
const seen = new Set;
|
|
8531
|
+
while (current2 !== undefined && !seen.has(current2.sourceNodeId)) {
|
|
8532
|
+
const source = graph.nodesById.get(current2.sourceNodeId);
|
|
8533
|
+
if (!source)
|
|
8534
|
+
return;
|
|
8535
|
+
seen.add(source.id);
|
|
8536
|
+
if (isExclusiveSplit(source, graph)) {
|
|
8537
|
+
return { splitId: source.id, port: current2.sourcePort };
|
|
8538
|
+
}
|
|
8539
|
+
const upstream = graph.incomingByTarget.get(source.id) ?? [];
|
|
8540
|
+
current2 = upstream.length === 1 ? upstream[0] : undefined;
|
|
8541
|
+
}
|
|
8542
|
+
return;
|
|
8543
|
+
}
|
|
8544
|
+
function findUnsatisfiableJoin(node, graph) {
|
|
8545
|
+
if (!isParallelJoin(node, graph))
|
|
8546
|
+
return;
|
|
8547
|
+
const incoming = graph.incomingByTarget.get(node.id) ?? [];
|
|
8548
|
+
if (incoming.length < 2)
|
|
8549
|
+
return;
|
|
8550
|
+
const origins = incoming.map((edge) => walkToExclusiveSplit(edge, graph));
|
|
8551
|
+
if (origins.includes(undefined))
|
|
8552
|
+
return;
|
|
8553
|
+
const resolved = origins;
|
|
8554
|
+
const splitIds = new Set(resolved.map((o) => o.splitId));
|
|
8555
|
+
if (splitIds.size !== 1)
|
|
8556
|
+
return;
|
|
8557
|
+
const ports = [...new Set(resolved.map((o) => o.port))].sort((a, b) => a.localeCompare(b));
|
|
8558
|
+
if (ports.length < 2)
|
|
8559
|
+
return;
|
|
8560
|
+
return { splitId: resolved[0].splitId, ports };
|
|
8561
|
+
}
|
|
8562
|
+
function describeUnsatisfiableJoin(node, finding, graph) {
|
|
8563
|
+
const split = graph.nodesById.get(finding.splitId);
|
|
8564
|
+
const splitLabel = split?.display?.label ?? finding.splitId;
|
|
8565
|
+
const joinLabel = node.display?.label ?? node.id;
|
|
8566
|
+
const splitKind = split?.type === "core.logic.switch" ? "Switch" : "Decision";
|
|
8567
|
+
const portList = finding.ports.map((p) => `"${p}"`).join(", ");
|
|
8568
|
+
return {
|
|
8569
|
+
path: `nodes[${node.id}]`,
|
|
8570
|
+
message: `Merge "${joinLabel}" can never complete: every one of its incoming paths comes from a ` + `different branch of the same ${splitKind} "${splitLabel}" (ports ${portList}). ` + `A Merge is a parallel join that waits for ALL incoming paths, but a ${splitKind} takes exactly one branch, ` + `so the run would stop at "${joinLabel}" and never reach an End node. ` + `Connect each branch directly to the node that should run next (a node accepts several incoming edges), ` + `and use Merge only to rejoin branches that run in parallel.`,
|
|
8571
|
+
severity: "error"
|
|
8572
|
+
};
|
|
8573
|
+
}
|
|
9299
8574
|
|
|
9300
|
-
export {
|
|
8575
|
+
export { connectorNodeValidator, FlowValidateService };
|
|
9301
8576
|
|
|
9302
|
-
//# debugId=
|
|
8577
|
+
//# debugId=17529DB3913E727964756E2164756E21
|