@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
|
@@ -0,0 +1,4234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_INTERVAL_MS,
|
|
3
|
+
DEFAULT_TIMEOUT_MS,
|
|
4
|
+
pendingSecondsFor,
|
|
5
|
+
pendingStallInstructions
|
|
6
|
+
} from "./packager-tool-tf3d2y8z.js";
|
|
7
|
+
import {
|
|
8
|
+
package_default
|
|
9
|
+
} from "./packager-tool-7b6tpg7y.js";
|
|
10
|
+
import {
|
|
11
|
+
VALID_RESOURCE_TYPES
|
|
12
|
+
} from "./packager-tool-27avrkyk.js";
|
|
13
|
+
import {
|
|
14
|
+
evaluatorTypeAliases
|
|
15
|
+
} from "./packager-tool-a1hbfmws.js";
|
|
16
|
+
import {
|
|
17
|
+
registerRegistryCommand,
|
|
18
|
+
registerSharedDebugInstanceCommands,
|
|
19
|
+
registerSharedIncidentCommands,
|
|
20
|
+
registerSharedInstanceCommands,
|
|
21
|
+
registerSharedProcessCommands,
|
|
22
|
+
registerSharedProcessesCommands
|
|
23
|
+
} from "./packager-tool-cdwpc9fh.js";
|
|
24
|
+
import {
|
|
25
|
+
setAgentProjectMetadataReader
|
|
26
|
+
} from "./packager-tool-v9psxdy5.js";
|
|
27
|
+
import {
|
|
28
|
+
ATTACHMENT_INSTRUCTIONS,
|
|
29
|
+
ATTACHMENT_UPLOAD_INSTRUCTIONS,
|
|
30
|
+
OutputFormatter,
|
|
31
|
+
RESULTS,
|
|
32
|
+
ScreenLogger,
|
|
33
|
+
appendOption,
|
|
34
|
+
catchError,
|
|
35
|
+
createHiddenDeprecatedTenantOption,
|
|
36
|
+
extractErrorMessage,
|
|
37
|
+
extractErrorMessageSync,
|
|
38
|
+
instructionsFor,
|
|
39
|
+
isPreviewBuild,
|
|
40
|
+
logger,
|
|
41
|
+
mapPackageMetadataOptions,
|
|
42
|
+
parseAttachmentSpec,
|
|
43
|
+
processContext,
|
|
44
|
+
registerPackageMetadataOptions,
|
|
45
|
+
resolveAttachmentInputs,
|
|
46
|
+
splitPackagedName,
|
|
47
|
+
summarizePlatformBody,
|
|
48
|
+
trackShipSucceeded
|
|
49
|
+
} from "./packager-tool-5qaxht3k.js";
|
|
50
|
+
import {
|
|
51
|
+
getFileSystem
|
|
52
|
+
} from "./packager-tool-0v3s8r01.js";
|
|
53
|
+
import {
|
|
54
|
+
__require
|
|
55
|
+
} from "./packager-tool-1yh8g550.js";
|
|
56
|
+
|
|
57
|
+
// src/commands/binding.ts
|
|
58
|
+
var BINDING_LIST_EXAMPLES = [
|
|
59
|
+
{
|
|
60
|
+
Description: "List all bindings in a flow file",
|
|
61
|
+
Command: "uip maestro flow binding list flow.json",
|
|
62
|
+
Output: {
|
|
63
|
+
Code: "BindingListSuccess",
|
|
64
|
+
Data: {
|
|
65
|
+
Bindings: [
|
|
66
|
+
{
|
|
67
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
68
|
+
name: "InvoiceProcess",
|
|
69
|
+
resource: "process",
|
|
70
|
+
defaultValue: "InvoiceProcess"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
Count: 1
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
var BINDING_ADD_EXAMPLES = [
|
|
79
|
+
{
|
|
80
|
+
Description: "Add a process binding to a flow",
|
|
81
|
+
Command: 'uip maestro flow binding add flow.json "Invoice" process "InvoiceProcess" --property-attribute name',
|
|
82
|
+
Output: {
|
|
83
|
+
Code: "BindingAddSuccess",
|
|
84
|
+
Data: {
|
|
85
|
+
Binding: {
|
|
86
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
87
|
+
name: "Invoice",
|
|
88
|
+
resource: "process",
|
|
89
|
+
defaultValue: "InvoiceProcess",
|
|
90
|
+
propertyAttribute: "name"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
];
|
|
96
|
+
var BINDING_DELETE_EXAMPLES = [
|
|
97
|
+
{
|
|
98
|
+
Description: "Remove all bindings by resource key",
|
|
99
|
+
Command: "uip maestro flow binding remove flow.json a1b2c3d4-0000-0000-0000-000000000001",
|
|
100
|
+
Output: {
|
|
101
|
+
Code: "BindingRemoveSuccess",
|
|
102
|
+
Data: {
|
|
103
|
+
DeletedBindings: [
|
|
104
|
+
{
|
|
105
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
106
|
+
name: "Invoice"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
Count: 1
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
var registerBindingCommand = (program) => {
|
|
115
|
+
const binding = program.command("binding").description("Manage bindings in a Flow file");
|
|
116
|
+
binding.command("list").description("List all bindings in a Flow file").argument("<file>", "Path to the .flow file").examples(BINDING_LIST_EXAMPLES).trackedAction(processContext, async (file) => {
|
|
117
|
+
const { listBindingsInFlow } = await import("./binding-service-dh91hw9e.js");
|
|
118
|
+
const [error, result] = await catchError(listBindingsInFlow(file));
|
|
119
|
+
if (error) {
|
|
120
|
+
OutputFormatter.error({
|
|
121
|
+
Result: RESULTS.Failure,
|
|
122
|
+
Message: "Error listing bindings",
|
|
123
|
+
Instructions: extractErrorMessageSync(error)
|
|
124
|
+
});
|
|
125
|
+
processContext.exit(1);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
OutputFormatter.success({
|
|
129
|
+
Result: RESULTS.Success,
|
|
130
|
+
Code: "BindingListSuccess",
|
|
131
|
+
Data: {
|
|
132
|
+
Bindings: result.bindings,
|
|
133
|
+
Count: result.count
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
binding.command("add").description(`Add a binding to a Flow file.
|
|
138
|
+
` + `Valid resource types: ${VALID_RESOURCE_TYPES.join(", ")}`).argument("<file>", "Path to the .flow file").argument("<name>", "Binding display name").argument("<resource>", "Resource type").argument("<default-value>", "Default value for the binding").option("--resource-key <key>", "Resource catalog identifier").option("--property-attribute <attr>", "Target node property (e.g., name, folderPath, connection)").option("--resource-sub-type <type>", "Resource sub-classification (e.g., Agent, Process)").examples(BINDING_ADD_EXAMPLES).trackedAction(processContext, async (file, name, resource, defaultValue, options) => {
|
|
139
|
+
const { addBindingToFlow } = await import("./binding-service-dh91hw9e.js");
|
|
140
|
+
const [error, result] = await catchError(addBindingToFlow(file, name, resource, defaultValue, {
|
|
141
|
+
resourceKey: options.resourceKey,
|
|
142
|
+
propertyAttribute: options.propertyAttribute,
|
|
143
|
+
resourceSubType: options.resourceSubType
|
|
144
|
+
}));
|
|
145
|
+
if (error) {
|
|
146
|
+
OutputFormatter.error({
|
|
147
|
+
Result: RESULTS.Failure,
|
|
148
|
+
Message: "Error adding binding",
|
|
149
|
+
Instructions: extractErrorMessageSync(error)
|
|
150
|
+
});
|
|
151
|
+
processContext.exit(1);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
OutputFormatter.success({
|
|
155
|
+
Result: RESULTS.Success,
|
|
156
|
+
Code: "BindingAddSuccess",
|
|
157
|
+
Data: {
|
|
158
|
+
Binding: result.binding
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
binding.command("remove").description("Remove all bindings with a given resource key from a Flow file").argument("<file>", "Path to the .flow file").argument("<resource-key>", "Resource key of the bindings to remove").examples(BINDING_DELETE_EXAMPLES).trackedAction(processContext, async (file, resourceKey) => {
|
|
163
|
+
const { deleteBindingFromFlow } = await import("./binding-service-dh91hw9e.js");
|
|
164
|
+
const [error, result] = await catchError(deleteBindingFromFlow(file, resourceKey));
|
|
165
|
+
if (error) {
|
|
166
|
+
OutputFormatter.error({
|
|
167
|
+
Result: RESULTS.Failure,
|
|
168
|
+
Message: "Error deleting binding",
|
|
169
|
+
Instructions: extractErrorMessageSync(error)
|
|
170
|
+
});
|
|
171
|
+
processContext.exit(1);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
OutputFormatter.success({
|
|
175
|
+
Result: RESULTS.Success,
|
|
176
|
+
Code: "BindingRemoveSuccess",
|
|
177
|
+
Data: {
|
|
178
|
+
DeletedBindings: result.deletedBindings,
|
|
179
|
+
Count: result.count
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// src/commands/debug.ts
|
|
186
|
+
function buildHttpFailureOutput(err) {
|
|
187
|
+
const stageLabel = err.stage ?? "request";
|
|
188
|
+
const where = `${err.method} ${err.endpoint}`;
|
|
189
|
+
const traceSuffix = err.parsedTraceId ? ` (traceId=${err.parsedTraceId})` : "";
|
|
190
|
+
const attemptSuffix = (err.attempts ?? 1) > 1 ? `, ${err.attempts} attempts` : "";
|
|
191
|
+
const Message = `Failed during ${stageLabel}: HTTP ${err.httpStatus} on ${where}${traceSuffix}${attemptSuffix}`;
|
|
192
|
+
const Instructions = pickInstructions(err);
|
|
193
|
+
const Context = {
|
|
194
|
+
httpStatus: err.httpStatus,
|
|
195
|
+
stage: err.stage,
|
|
196
|
+
endpoint: err.endpoint,
|
|
197
|
+
method: err.method
|
|
198
|
+
};
|
|
199
|
+
if (err.parsedErrorCode !== undefined) {
|
|
200
|
+
Context.errorCode = err.parsedErrorCode;
|
|
201
|
+
}
|
|
202
|
+
if (err.parsedRequestId !== undefined) {
|
|
203
|
+
Context.requestId = err.parsedRequestId;
|
|
204
|
+
}
|
|
205
|
+
if (err.parsedTraceId !== undefined) {
|
|
206
|
+
Context.traceId = err.parsedTraceId;
|
|
207
|
+
}
|
|
208
|
+
if (err.attempts !== undefined) {
|
|
209
|
+
Context.attempts = err.attempts;
|
|
210
|
+
}
|
|
211
|
+
const output = {
|
|
212
|
+
Result: RESULTS.Failure,
|
|
213
|
+
Message,
|
|
214
|
+
Instructions,
|
|
215
|
+
Context
|
|
216
|
+
};
|
|
217
|
+
const Retry = pickRetryHint(err);
|
|
218
|
+
if (Retry !== undefined) {
|
|
219
|
+
output.Retry = Retry;
|
|
220
|
+
}
|
|
221
|
+
return output;
|
|
222
|
+
}
|
|
223
|
+
function pickRetryHint(err) {
|
|
224
|
+
if (err.httpStatus < 500 || err.httpStatus >= 600) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
return (err.attempts ?? 1) > 1 ? "RetryWillNotFix" : "RetryAfter30Seconds";
|
|
228
|
+
}
|
|
229
|
+
function pickInstructions(err) {
|
|
230
|
+
const isOpaque5xx = err.httpStatus >= 500 && err.httpStatus < 600 && (err.parsedErrorCode === undefined || err.parsedErrorCode === "0");
|
|
231
|
+
if (isOpaque5xx) {
|
|
232
|
+
const stage = err.stage ?? "request";
|
|
233
|
+
const traceIds = err.traceIds?.length ? err.traceIds : err.parsedTraceId ? [err.parsedTraceId] : [];
|
|
234
|
+
const traceHint = traceIds.length ? ` traceIds: ${traceIds.join(", ")}.` : "";
|
|
235
|
+
const escalationTraces = traceIds.length ? ` and traceIds: ${traceIds.join(", ")}` : "";
|
|
236
|
+
if ((err.attempts ?? 1) > 1) {
|
|
237
|
+
return `${err.method} ${err.endpoint} returned ${err.httpStatus} on all ${err.attempts} attempts ` + `during '${stage}', so the backend runtime is down rather than blipping. ` + `Do not re-run 'debug' — escalate to the owning service team with the endpoint${escalationTraces}. ` + `To keep working meanwhile, run the project instead of debugging it: ` + `'uip solution pack <solution-path> <out-dir>', 'uip solution publish <out-dir>/<name>.zip', ` + `'uip solution deploy run', then 'uip maestro flow process run'.`;
|
|
238
|
+
}
|
|
239
|
+
return `The backend returned an unstructured ${err.httpStatus} during '${stage}'.${traceHint} ` + `This is often transient — wait ~30s and retry once. If it fails again, capture the traceId ` + `and ${err.method} ${err.endpoint} and escalate to the owning service team.`;
|
|
240
|
+
}
|
|
241
|
+
if (err.httpStatus === 401 || err.httpStatus === 403) {
|
|
242
|
+
return `Authentication/authorization rejected by ${err.method} ${err.endpoint}. ` + `Run 'uip login' and verify the active tenant has access.`;
|
|
243
|
+
}
|
|
244
|
+
if (err.httpStatus === 404) {
|
|
245
|
+
const isStructured = err.parsedErrorMessage !== undefined || err.parsedErrorCode !== undefined;
|
|
246
|
+
if (!isStructured) {
|
|
247
|
+
const body = summarizePlatformBody(err.body);
|
|
248
|
+
const bodySuffix = body ? ` Platform response: "${body}".` : "";
|
|
249
|
+
return `${err.method} ${err.endpoint} returned 404 during '${err.stage ?? "request"}', ` + `but the response was not a structured Orchestrator error — the request likely ` + `did not reach the tenant. This usually means the organization or tenant in your ` + `login session is wrong or was renamed. Run 'uip login tenant list' to see the ` + `current tenant names, re-select with 'uip login tenant set <name>' (or re-run ` + `'uip login'), then retry. Do not create a new workspace or retry as-is — the ` + `resource likely exists under the current tenant name.${bodySuffix}`;
|
|
250
|
+
}
|
|
251
|
+
const detail2 = err.parsedErrorMessage ? ` Backend reported: "${err.parsedErrorMessage}".` : "";
|
|
252
|
+
return `${err.method} ${err.endpoint} returned 404 during '${err.stage ?? "request"}'. ` + `The target resource may not exist — verify upstream IDs (solutionId, instanceId) ` + `and re-run.${detail2}`;
|
|
253
|
+
}
|
|
254
|
+
const detail = err.parsedErrorMessage ? ` Backend reported: "${err.parsedErrorMessage}".` : "";
|
|
255
|
+
return `${err.method} ${err.endpoint} failed with HTTP ${err.httpStatus} during ` + `'${err.stage ?? "request"}'.${detail} Inspect the response body for details ` + `and retry once before reporting.`;
|
|
256
|
+
}
|
|
257
|
+
function summarizeIncidents(incidents) {
|
|
258
|
+
return incidents.map((inc) => {
|
|
259
|
+
const code = inc.errorCode ? `[${inc.errorCode}] ` : "";
|
|
260
|
+
const element = inc.elementId ? ` (element ${inc.elementId})` : "";
|
|
261
|
+
return `${code}${inc.errorMessage}${element}`;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function buildIncidentInstructions(incidents, instanceId) {
|
|
265
|
+
const lines = summarizeIncidents(incidents);
|
|
266
|
+
const first = incidents[0];
|
|
267
|
+
const traceHint = first?.traceId ? ` traceId=${first.traceId}.` : "";
|
|
268
|
+
const detailHint = `Run 'uip maestro flow debug-instance incidents ${instanceId}' for ` + `the full backend payload (incidentId, errorDetails, AI summary).`;
|
|
269
|
+
return `Incident(s):
|
|
270
|
+
- ${lines.join(`
|
|
271
|
+
- `)}
|
|
272
|
+
${detailHint}${traceHint}`;
|
|
273
|
+
}
|
|
274
|
+
var CONVERSATIONAL_DEBUG_WARNING = "This flow, which starts on a conversation trigger, can only be debugged from a chat UI. " + `Debug it using the chat panel from either:
|
|
275
|
+
` + " 1. Studio Web — open the debug URL printed once the solution uploads " + `(also 'Data.studioWebUrl' in the result).
|
|
276
|
+
` + " 2. UiPath Maestro VS Code extension — open the flow and hit Debug.";
|
|
277
|
+
async function projectStartsOnConversationTrigger(projectPath) {
|
|
278
|
+
const [error, found] = await catchError((async () => {
|
|
279
|
+
const { readFlowWorkflow, resolveProjectFlowFile } = await import("./flow-io-r318sjrw.js");
|
|
280
|
+
const { startsOnConversationTrigger } = await import("./debug-conversation-trigger-452frzyz.js");
|
|
281
|
+
const flowFilePath = await resolveProjectFlowFile(projectPath);
|
|
282
|
+
const { workflow } = await readFlowWorkflow(flowFilePath);
|
|
283
|
+
return startsOnConversationTrigger(workflow);
|
|
284
|
+
})());
|
|
285
|
+
if (error) {
|
|
286
|
+
logger.info(`Skipped the conversational pre-flight scan: ${error.message}`);
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return found === true;
|
|
290
|
+
}
|
|
291
|
+
var FLOW_DEBUG_EXAMPLES = [
|
|
292
|
+
{
|
|
293
|
+
Description: "Debug a local flow project via Studio Web",
|
|
294
|
+
Command: "uip maestro flow debug ./my-flow-project",
|
|
295
|
+
Output: {
|
|
296
|
+
Code: "FlowDebug",
|
|
297
|
+
Data: {
|
|
298
|
+
jobKey: "b2c3d4e5-0000-0000-0000-000000000001",
|
|
299
|
+
instanceId: "c3d4e5f6-0000-0000-0000-000000000001",
|
|
300
|
+
runId: "d4e5f6a7-0000-0000-0000-000000000001",
|
|
301
|
+
finalStatus: "Completed",
|
|
302
|
+
solutionId: "e5f6a7b8-0000-0000-0000-000000000001",
|
|
303
|
+
studioWebUrl: "https://cloud.uipath.com/org/tenant/studio_/debug/e5f6a7b8",
|
|
304
|
+
elementExecutions: [
|
|
305
|
+
{ elementId: "Node_1", status: "Completed" }
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
Description: "Conversational flow (core.trigger.conversation) — uploaded to Studio Web and handed off, since only a chat UI can drive it. No debug session is started",
|
|
312
|
+
Command: "uip maestro flow debug ./my-chat-flow --open-in-browser",
|
|
313
|
+
Output: {
|
|
314
|
+
Code: "FlowDebugStudioWebHandoff",
|
|
315
|
+
Data: {
|
|
316
|
+
solutionId: "e5f6a7b8-0000-0000-0000-000000000001",
|
|
317
|
+
studioWebUrl: "https://cloud.uipath.com/org/studio_/designer/a1b2c3d4?solutionId=e5f6a7b8",
|
|
318
|
+
handedOff: true
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
Description: 'Faulted debug — emitted as a Result: "Failure" envelope (exit 1) with incident details (errorCode, errorMessage, elementId) inline on Data.incidents, so no follow-up command is needed',
|
|
324
|
+
Command: "uip maestro flow debug ./my-flow-project",
|
|
325
|
+
Output: {
|
|
326
|
+
Code: "FlowDebug",
|
|
327
|
+
Data: {
|
|
328
|
+
jobKey: "b2c3d4e5-0000-0000-0000-000000000001",
|
|
329
|
+
instanceId: "c3d4e5f6-0000-0000-0000-000000000001",
|
|
330
|
+
runId: "d4e5f6a7-0000-0000-0000-000000000001",
|
|
331
|
+
finalStatus: "Faulted",
|
|
332
|
+
incidents: [
|
|
333
|
+
{
|
|
334
|
+
incidentId: "inc-1",
|
|
335
|
+
elementId: "Node_3",
|
|
336
|
+
errorCode: "Process.UnhandledException",
|
|
337
|
+
errorMessage: "Object reference not set to an instance of an object."
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
];
|
|
344
|
+
var LOGIN_INSTRUCTIONS = "Run 'uip login' and select an organization, tenant, and folder with Studio Web access, then retry.";
|
|
345
|
+
var INPUT_INSTRUCTIONS = "Provide --inputs as a valid JSON object or a readable @file.json path.";
|
|
346
|
+
var NUMBER_OPTION_INSTRUCTIONS = "Use a positive numeric value. Provide --poll-interval in milliseconds and --timeout in seconds.";
|
|
347
|
+
var FOLDER_ID_INSTRUCTIONS = "Provide --folder-id as a positive integer (the Orchestrator folder Id, e.g. 12345).";
|
|
348
|
+
var FOLDER_TARGETING_INSTRUCTIONS = "Pass at most one of --folder-id, --folder-key, or --folder-path. Use 'uip or folders list' to discover values.";
|
|
349
|
+
var FOLDER_RESOLUTION_INSTRUCTIONS = "Provide a valid --folder-key (UUID) or --folder-path. Use 'uip or folders list' to confirm the key or path.";
|
|
350
|
+
var FOLDER_RESOLUTION_NETWORK_INSTRUCTIONS = "The folder lookup itself failed (network / Orchestrator API). Re-authenticate with 'uip login', verify connectivity, and retry.";
|
|
351
|
+
var DEBUG_FAILURE_INSTRUCTIONS = "Check that the flow project is valid, the selected folder is accessible, and Studio Web debug is available, then retry.";
|
|
352
|
+
async function openInBrowserOrWarn(url) {
|
|
353
|
+
const [openError] = await catchError(async () => {
|
|
354
|
+
await getFileSystem().utils.open(url);
|
|
355
|
+
});
|
|
356
|
+
if (openError) {
|
|
357
|
+
OutputFormatter.log({
|
|
358
|
+
Warning: `Could not open the browser automatically: ${openError.message}`
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function buildHandoffInstructions(studioWebUrl) {
|
|
363
|
+
return "This flow can only be driven from a chat UI, so no debug session was started. " + `Debug it using the chat panel from either:
|
|
364
|
+
` + ` 1. Studio Web — open ${studioWebUrl}.
|
|
365
|
+
` + " 2. UiPath Maestro VS Code extension — open the flow and hit Debug.";
|
|
366
|
+
}
|
|
367
|
+
function isFolderInputError(err) {
|
|
368
|
+
if ("response" in err)
|
|
369
|
+
return false;
|
|
370
|
+
if (err.name === "FetchError")
|
|
371
|
+
return false;
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
function parsePositiveNumberOption(value, optionName) {
|
|
375
|
+
const parsed = Number(value);
|
|
376
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
377
|
+
return {
|
|
378
|
+
ok: false,
|
|
379
|
+
message: `${optionName} must be a positive number.`
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return { ok: true, value: parsed };
|
|
383
|
+
}
|
|
384
|
+
function parsePositiveIntegerOption(value, optionName) {
|
|
385
|
+
const parsed = Number(value);
|
|
386
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
387
|
+
return {
|
|
388
|
+
ok: false,
|
|
389
|
+
message: `${optionName} must be a positive integer.`
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
return { ok: true, value: parsed };
|
|
393
|
+
}
|
|
394
|
+
function registerDebugCommand(program) {
|
|
395
|
+
program.command("debug").description("Debug a flow project by uploading to Studio Web and running a debug session").argument("<project-path>", "Path to the flow project directory (must contain project.uiproj)").option("--folder-id <id>", "Orchestrator folder ID (OrganizationUnitId) as a positive integer. Mutually exclusive with --folder-key/--folder-path. Auto-detected if none are provided.").option("--folder-key <key>", "Orchestrator folder key (UUID). The CLI resolves it to the folder ID. Mutually exclusive with --folder-id/--folder-path.").option("--folder-path <path>", "Orchestrator folder path (e.g. 'Shared/Sub'). The CLI resolves it to the folder ID. Mutually exclusive with --folder-id/--folder-key.").option("--poll-interval <ms>", "Polling interval in milliseconds", "2000").option("--timeout <seconds>", "Maximum seconds to wait for debug completion").option("-i, --inputs <json>", "Input parameters as JSON string or @file.json").option("--attachment <name=path>", "Upload a file as a file-typed workflow input variable (repeatable). " + "Example: --attachment file1=./resume.pdf", appendOption, []).option("--open-in-browser", "Open the Studio Web URL in the browser once it is known.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(FLOW_DEBUG_EXAMPLES).trackedAction(processContext, async (projectPath, options) => {
|
|
396
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
397
|
+
const isConversational = await projectStartsOnConversationTrigger(projectPath);
|
|
398
|
+
if (isConversational) {
|
|
399
|
+
OutputFormatter.log({
|
|
400
|
+
Warning: CONVERSATIONAL_DEBUG_WARNING
|
|
401
|
+
});
|
|
402
|
+
if (options.timeout !== undefined) {
|
|
403
|
+
OutputFormatter.log({
|
|
404
|
+
Warning: "--timeout has no effect here: the CLI uploads and hands off without polling a run."
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
let urlReported = false;
|
|
409
|
+
const [loginError, login] = await catchError(getLoginStatusAsync({
|
|
410
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
411
|
+
}));
|
|
412
|
+
if (loginError) {
|
|
413
|
+
OutputFormatter.error({
|
|
414
|
+
Result: RESULTS.Failure,
|
|
415
|
+
Message: loginError.message,
|
|
416
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
417
|
+
});
|
|
418
|
+
processContext.exit(1);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const {
|
|
422
|
+
loginStatus,
|
|
423
|
+
baseUrl,
|
|
424
|
+
accessToken,
|
|
425
|
+
organizationId,
|
|
426
|
+
tenantId,
|
|
427
|
+
tenantName,
|
|
428
|
+
organizationUnitId,
|
|
429
|
+
organizationName
|
|
430
|
+
} = login;
|
|
431
|
+
if (loginStatus !== "Logged in" || !baseUrl) {
|
|
432
|
+
OutputFormatter.error({
|
|
433
|
+
Result: RESULTS.Failure,
|
|
434
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
435
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
436
|
+
});
|
|
437
|
+
processContext.exit(1);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (!organizationId) {
|
|
441
|
+
OutputFormatter.error({
|
|
442
|
+
Result: RESULTS.Failure,
|
|
443
|
+
Message: "Organization ID not available from login.",
|
|
444
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
445
|
+
});
|
|
446
|
+
processContext.exit(1);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (!tenantId) {
|
|
450
|
+
OutputFormatter.error({
|
|
451
|
+
Result: RESULTS.Failure,
|
|
452
|
+
Message: "Tenant ID not available from login.",
|
|
453
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
454
|
+
});
|
|
455
|
+
processContext.exit(1);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (!tenantName) {
|
|
459
|
+
OutputFormatter.error({
|
|
460
|
+
Result: RESULTS.Failure,
|
|
461
|
+
Message: "Tenant name not available from login.",
|
|
462
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
463
|
+
});
|
|
464
|
+
processContext.exit(1);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (!accessToken) {
|
|
468
|
+
OutputFormatter.error({
|
|
469
|
+
Result: RESULTS.Failure,
|
|
470
|
+
Message: "Access token not available from login.",
|
|
471
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
472
|
+
});
|
|
473
|
+
processContext.exit(1);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (!organizationName) {
|
|
477
|
+
OutputFormatter.error({
|
|
478
|
+
Result: RESULTS.Failure,
|
|
479
|
+
Message: "Organization name not available from login. Required for Studio Web debug.",
|
|
480
|
+
Instructions: LOGIN_INSTRUCTIONS
|
|
481
|
+
});
|
|
482
|
+
processContext.exit(1);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const folderTargetingFlags = [
|
|
486
|
+
options.folderId !== undefined ? "--folder-id" : undefined,
|
|
487
|
+
options.folderKey !== undefined ? "--folder-key" : undefined,
|
|
488
|
+
options.folderPath !== undefined ? "--folder-path" : undefined
|
|
489
|
+
].filter((flag) => flag !== undefined);
|
|
490
|
+
if (folderTargetingFlags.length > 1) {
|
|
491
|
+
OutputFormatter.error({
|
|
492
|
+
Result: RESULTS.ValidationError,
|
|
493
|
+
Message: `Cannot combine ${folderTargetingFlags.join(", ")}.`,
|
|
494
|
+
Instructions: FOLDER_TARGETING_INSTRUCTIONS
|
|
495
|
+
});
|
|
496
|
+
processContext.exit(3);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
let userProvidedFolderId;
|
|
500
|
+
if (options.folderId !== undefined) {
|
|
501
|
+
const parsed = parsePositiveIntegerOption(options.folderId, "--folder-id");
|
|
502
|
+
if (!parsed.ok) {
|
|
503
|
+
OutputFormatter.error({
|
|
504
|
+
Result: RESULTS.ValidationError,
|
|
505
|
+
Message: parsed.message,
|
|
506
|
+
Instructions: FOLDER_ID_INSTRUCTIONS
|
|
507
|
+
});
|
|
508
|
+
processContext.exit(3);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
userProvidedFolderId = parsed.value;
|
|
512
|
+
} else if (options.folderKey !== undefined || options.folderPath !== undefined) {
|
|
513
|
+
const { resolveFolder } = await import("./index-ev4mdg5y.js");
|
|
514
|
+
const [folderError, folder] = await catchError(resolveFolder({
|
|
515
|
+
folderKey: options.folderKey,
|
|
516
|
+
folderPath: options.folderPath
|
|
517
|
+
}, { loginValidity: options.loginValidity }));
|
|
518
|
+
if (folderError || !folder) {
|
|
519
|
+
const inputProblem = !folderError || isFolderInputError(folderError);
|
|
520
|
+
const message = folderError ? `Could not resolve folder: ${folderError.message}` : "Folder targeting requested but no folder resolved.";
|
|
521
|
+
OutputFormatter.error({
|
|
522
|
+
Result: inputProblem ? RESULTS.ValidationError : RESULTS.Failure,
|
|
523
|
+
Message: message,
|
|
524
|
+
Instructions: inputProblem ? FOLDER_RESOLUTION_INSTRUCTIONS : FOLDER_RESOLUTION_NETWORK_INSTRUCTIONS
|
|
525
|
+
});
|
|
526
|
+
processContext.exit(inputProblem ? 3 : 1);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
userProvidedFolderId = folder.id;
|
|
530
|
+
logger.info(`Resolved folder '${folder.path}' to ID ${folder.id} (key ${folder.key}).`);
|
|
531
|
+
}
|
|
532
|
+
const studioWebConfig = {
|
|
533
|
+
baseUrl,
|
|
534
|
+
organizationId,
|
|
535
|
+
tenantId,
|
|
536
|
+
tenantName,
|
|
537
|
+
authToken: accessToken,
|
|
538
|
+
organizationUnitId: userProvidedFolderId ?? organizationUnitId,
|
|
539
|
+
organizationName,
|
|
540
|
+
userProvidedFolderId
|
|
541
|
+
};
|
|
542
|
+
logger.info("Login verified successfully.");
|
|
543
|
+
logger.info(` Organization: ${organizationName} (${organizationId})`);
|
|
544
|
+
logger.info(` Tenant: ${tenantName} (${tenantId})`);
|
|
545
|
+
logger.info(` Base URL: ${baseUrl}`);
|
|
546
|
+
const {
|
|
547
|
+
FlowDebugService,
|
|
548
|
+
InboundVoiceDebugError,
|
|
549
|
+
MissingSolutionWrapperError,
|
|
550
|
+
UnregisteredProjectError
|
|
551
|
+
} = await import("./flow-debug-service-tfbefas6.js");
|
|
552
|
+
const { MaestroHttpError } = await import("./debug-http-client-wd6xb8v4.js");
|
|
553
|
+
const {
|
|
554
|
+
buildPollTimeoutFailureOutput,
|
|
555
|
+
DebugPollTimeoutError,
|
|
556
|
+
isSuccessfulDebugStatus
|
|
557
|
+
} = await import("./debug-service-fvyexnb1.js");
|
|
558
|
+
const {
|
|
559
|
+
buildSolutionUploadFailureOutput,
|
|
560
|
+
isSolutionUploadHttpError
|
|
561
|
+
} = await import("./index-n33np76r.js");
|
|
562
|
+
const service = new FlowDebugService;
|
|
563
|
+
const parsedPollInterval = parsePositiveNumberOption(options.pollInterval || "2000", "--poll-interval");
|
|
564
|
+
if (!parsedPollInterval.ok) {
|
|
565
|
+
OutputFormatter.error({
|
|
566
|
+
Result: RESULTS.Failure,
|
|
567
|
+
Message: parsedPollInterval.message,
|
|
568
|
+
Instructions: NUMBER_OPTION_INSTRUCTIONS
|
|
569
|
+
});
|
|
570
|
+
processContext.exit(1);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const pollInterval = parsedPollInterval.value;
|
|
574
|
+
let maxPolls;
|
|
575
|
+
if (options.timeout !== undefined) {
|
|
576
|
+
const parsedTimeout = parsePositiveNumberOption(options.timeout, "--timeout");
|
|
577
|
+
if (!parsedTimeout.ok) {
|
|
578
|
+
OutputFormatter.error({
|
|
579
|
+
Result: RESULTS.Failure,
|
|
580
|
+
Message: parsedTimeout.message,
|
|
581
|
+
Instructions: NUMBER_OPTION_INSTRUCTIONS
|
|
582
|
+
});
|
|
583
|
+
processContext.exit(1);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
maxPolls = Math.max(1, Math.ceil(parsedTimeout.value * 1000 / pollInterval));
|
|
587
|
+
}
|
|
588
|
+
let inputs;
|
|
589
|
+
if (options.inputs) {
|
|
590
|
+
const { parseFlowInputs } = await import("./flow-api-wv8p8xbd.js");
|
|
591
|
+
const [parseError, parsed] = await catchError(parseFlowInputs(options.inputs));
|
|
592
|
+
if (parseError) {
|
|
593
|
+
OutputFormatter.error({
|
|
594
|
+
Result: RESULTS.Failure,
|
|
595
|
+
Message: `Failed to parse inputs: ${parseError.message}`,
|
|
596
|
+
Instructions: INPUT_INSTRUCTIONS
|
|
597
|
+
});
|
|
598
|
+
processContext.exit(1);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
inputs = parsed;
|
|
602
|
+
}
|
|
603
|
+
const attachmentSpecs = options.attachment ?? [];
|
|
604
|
+
if (attachmentSpecs.length > 0) {
|
|
605
|
+
const [parseError, specs] = catchError(() => attachmentSpecs.map(parseAttachmentSpec));
|
|
606
|
+
if (parseError) {
|
|
607
|
+
OutputFormatter.error({
|
|
608
|
+
Result: RESULTS.Failure,
|
|
609
|
+
Message: parseError.message,
|
|
610
|
+
Instructions: ATTACHMENT_INSTRUCTIONS
|
|
611
|
+
});
|
|
612
|
+
processContext.exit(1);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const { uploadJobAttachment } = await import("./index-ev4mdg5y.js");
|
|
616
|
+
const [uploadError, resolved] = await catchError(resolveAttachmentInputs(specs, (bytes, fileName) => uploadJobAttachment(bytes, fileName, {
|
|
617
|
+
tenant: tenantName
|
|
618
|
+
})));
|
|
619
|
+
if (uploadError) {
|
|
620
|
+
OutputFormatter.error({
|
|
621
|
+
Result: RESULTS.Failure,
|
|
622
|
+
Message: uploadError.message,
|
|
623
|
+
Instructions: ATTACHMENT_UPLOAD_INSTRUCTIONS
|
|
624
|
+
});
|
|
625
|
+
processContext.exit(1);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
inputs = inputs ?? {};
|
|
629
|
+
for (const { name, path, reference } of resolved) {
|
|
630
|
+
if (name in inputs) {
|
|
631
|
+
logger.warn(`--attachment '${name}' overrides the value provided in --inputs.`);
|
|
632
|
+
}
|
|
633
|
+
inputs[name] = reference;
|
|
634
|
+
logger.info(`Uploaded '${path}' as attachment ${reference.ID} (variable '${name}').`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (inputs !== undefined) {
|
|
638
|
+
logger.info(`Inputs: ${JSON.stringify(inputs)}`);
|
|
639
|
+
}
|
|
640
|
+
const debugOptions = {
|
|
641
|
+
inputs,
|
|
642
|
+
...maxPolls === undefined ? {} : { maxPolls },
|
|
643
|
+
...isConversational ? { uploadOnly: true } : {}
|
|
644
|
+
};
|
|
645
|
+
logger.info(`Starting Studio Web debug session for: ${projectPath}`);
|
|
646
|
+
const [executeError, result] = await catchError(service.executeProjectViaStudioWeb(projectPath, studioWebConfig, {
|
|
647
|
+
onStatusChange: (st, executions) => {
|
|
648
|
+
const completed = executions.filter((e) => e.status === "Completed").length;
|
|
649
|
+
const total = executions.length;
|
|
650
|
+
logger.info(`Status: ${st} (${completed}/${total} elements completed)`);
|
|
651
|
+
for (const exec of executions) {
|
|
652
|
+
const icon = exec.status === "Completed" ? "v" : exec.status === "InProgress" ? ">" : "-";
|
|
653
|
+
logger.info(` ${icon} ${exec.elementId} [${exec.status}]`);
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
onIncident: (incidents2) => {
|
|
657
|
+
logger.warn(`Incidents: ${JSON.stringify(incidents2)}`);
|
|
658
|
+
},
|
|
659
|
+
onStudioWebUrl: (url) => {
|
|
660
|
+
if (options.openInBrowser !== true)
|
|
661
|
+
return;
|
|
662
|
+
if (urlReported)
|
|
663
|
+
return;
|
|
664
|
+
urlReported = true;
|
|
665
|
+
OutputFormatter.log({
|
|
666
|
+
Message: `Studio Web debug URL: ${url}`
|
|
667
|
+
});
|
|
668
|
+
openInBrowserOrWarn(url);
|
|
669
|
+
}
|
|
670
|
+
}, pollInterval, debugOptions));
|
|
671
|
+
if (executeError) {
|
|
672
|
+
if (executeError instanceof MissingSolutionWrapperError) {
|
|
673
|
+
OutputFormatter.error({
|
|
674
|
+
Result: RESULTS.ValidationError,
|
|
675
|
+
Message: executeError.message,
|
|
676
|
+
Instructions: executeError.instructions
|
|
677
|
+
});
|
|
678
|
+
processContext.exit(3);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
if (executeError instanceof InboundVoiceDebugError) {
|
|
682
|
+
OutputFormatter.error({
|
|
683
|
+
Result: RESULTS.ValidationError,
|
|
684
|
+
Message: executeError.message,
|
|
685
|
+
Instructions: executeError.instructions
|
|
686
|
+
});
|
|
687
|
+
processContext.exit(3);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (executeError instanceof UnregisteredProjectError) {
|
|
691
|
+
OutputFormatter.error({
|
|
692
|
+
Result: RESULTS.ValidationError,
|
|
693
|
+
Message: executeError.message,
|
|
694
|
+
Instructions: executeError.instructions
|
|
695
|
+
});
|
|
696
|
+
processContext.exit(3);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (executeError instanceof DebugPollTimeoutError) {
|
|
700
|
+
OutputFormatter.error(buildPollTimeoutFailureOutput(executeError, "uip maestro flow debug-instance incidents"));
|
|
701
|
+
} else if (executeError instanceof MaestroHttpError) {
|
|
702
|
+
OutputFormatter.error(buildHttpFailureOutput(executeError));
|
|
703
|
+
} else if (isSolutionUploadHttpError(executeError)) {
|
|
704
|
+
OutputFormatter.error(buildSolutionUploadFailureOutput(executeError, {
|
|
705
|
+
subject: "flow",
|
|
706
|
+
importCheckHint: "Run 'uip maestro flow validate' to confirm the project is well-formed."
|
|
707
|
+
}));
|
|
708
|
+
} else {
|
|
709
|
+
OutputFormatter.error({
|
|
710
|
+
Result: RESULTS.Failure,
|
|
711
|
+
Message: executeError.message,
|
|
712
|
+
Instructions: DEBUG_FAILURE_INSTRUCTIONS
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
processContext.exit(1);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if ("uploadedOnly" in result) {
|
|
719
|
+
OutputFormatter.success({
|
|
720
|
+
Result: RESULTS.Success,
|
|
721
|
+
Code: "FlowDebugStudioWebHandoff",
|
|
722
|
+
Data: {
|
|
723
|
+
solutionId: result.solutionId,
|
|
724
|
+
studioWebUrl: result.studioWebUrl,
|
|
725
|
+
handedOff: true
|
|
726
|
+
},
|
|
727
|
+
Instructions: buildHandoffInstructions(result.studioWebUrl)
|
|
728
|
+
}, { preserveDataKeys: true });
|
|
729
|
+
processContext.exit(0);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const success = isSuccessfulDebugStatus(result.finalStatus);
|
|
733
|
+
const data = {
|
|
734
|
+
jobKey: result.jobKey,
|
|
735
|
+
instanceId: result.instanceId,
|
|
736
|
+
runId: result.runId,
|
|
737
|
+
folderKey: result.folderKey,
|
|
738
|
+
finalStatus: result.finalStatus,
|
|
739
|
+
solutionId: result.solutionId,
|
|
740
|
+
studioWebUrl: result.studioWebUrl,
|
|
741
|
+
elementExecutions: result.elementExecutions,
|
|
742
|
+
variables: result.variables,
|
|
743
|
+
variablesError: result.variablesError,
|
|
744
|
+
incidents: result.incidents
|
|
745
|
+
};
|
|
746
|
+
if (success) {
|
|
747
|
+
const instructions = result.variablesError ? `Debug completed with status: ${result.finalStatus}, but the output variables could not be read after ${result.variablesError.attempts} attempt(s): ${result.variablesError.message}. ` + `Data.variables is absent and Data.variablesError explains why — treat the outputs as UNKNOWN, not empty. ` + `Retry with 'uip maestro flow debug-instance variables ${result.instanceId}'.` : `Debug completed with status: ${result.finalStatus}`;
|
|
748
|
+
OutputFormatter.success({
|
|
749
|
+
Result: RESULTS.Success,
|
|
750
|
+
Code: "FlowDebug",
|
|
751
|
+
Data: data,
|
|
752
|
+
Instructions: instructions
|
|
753
|
+
}, { preserveDataKeys: true });
|
|
754
|
+
processContext.exit(0);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const incidents = result.incidents ?? [];
|
|
758
|
+
const summary = summarizeIncidents(incidents);
|
|
759
|
+
const Message = incidents.length ? `Debug session ${result.finalStatus}. ${summary[0]}` : `Debug session ${result.finalStatus}. No incident details returned by PIMS.`;
|
|
760
|
+
const Instructions = incidents.length ? buildIncidentInstructions(incidents, result.instanceId) : `Run 'uip maestro flow debug-instance incidents ${result.instanceId}' to inspect the backend payload, then 'uip maestro flow incident summary' to confirm whether the process recorded a tenant-level incident.`;
|
|
761
|
+
const Context = {};
|
|
762
|
+
if (incidents[0]?.errorCode) {
|
|
763
|
+
Context.errorCode = incidents[0].errorCode;
|
|
764
|
+
}
|
|
765
|
+
if (incidents[0]?.traceId) {
|
|
766
|
+
Context.traceId = incidents[0].traceId;
|
|
767
|
+
}
|
|
768
|
+
OutputFormatter.error({
|
|
769
|
+
Result: RESULTS.Failure,
|
|
770
|
+
Code: "FlowDebug",
|
|
771
|
+
Message,
|
|
772
|
+
Instructions,
|
|
773
|
+
...Object.keys(Context).length > 0 ? { Context } : {},
|
|
774
|
+
Data: data
|
|
775
|
+
});
|
|
776
|
+
processContext.exit(1);
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
// src/commands/debug-instances.ts
|
|
780
|
+
var registerDebugInstancesCommand = (program) => {
|
|
781
|
+
registerSharedDebugInstanceCommands(program, {
|
|
782
|
+
toolLabel: "Flow"
|
|
783
|
+
});
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/commands/edge.ts
|
|
787
|
+
var EDGE_LIST_EXAMPLES = [
|
|
788
|
+
{
|
|
789
|
+
Description: "List all edges in a flow file",
|
|
790
|
+
Command: "uip maestro flow edge list flow.json",
|
|
791
|
+
Output: {
|
|
792
|
+
Code: "EdgeListSuccess",
|
|
793
|
+
Data: {
|
|
794
|
+
Edges: [
|
|
795
|
+
{
|
|
796
|
+
id: "edge_a1b2c3d4",
|
|
797
|
+
source: "Node_1",
|
|
798
|
+
target: "Node_2"
|
|
799
|
+
}
|
|
800
|
+
],
|
|
801
|
+
Count: 1
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
];
|
|
806
|
+
var EDGE_ADD_EXAMPLES = [
|
|
807
|
+
{
|
|
808
|
+
Description: "Add an edge between two nodes in a flow",
|
|
809
|
+
Command: "uip maestro flow edge add flow.json Node_1 Node_2",
|
|
810
|
+
Output: {
|
|
811
|
+
Code: "EdgeAddSuccess",
|
|
812
|
+
Data: {
|
|
813
|
+
Edge: {
|
|
814
|
+
id: "edge_a1b2c3d4",
|
|
815
|
+
source: "Node_1",
|
|
816
|
+
target: "Node_2",
|
|
817
|
+
sourcePort: "output",
|
|
818
|
+
targetPort: "input"
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
Description: "Re-running with the same arguments is idempotent: returns the existing edge id with code EdgeAlreadyExists and a stderr warning.",
|
|
825
|
+
Command: "uip maestro flow edge add flow.json Node_1 Node_2",
|
|
826
|
+
Output: {
|
|
827
|
+
Code: "EdgeAlreadyExists",
|
|
828
|
+
Data: {
|
|
829
|
+
Edge: {
|
|
830
|
+
id: "edge_a1b2c3d4",
|
|
831
|
+
source: "Node_1",
|
|
832
|
+
target: "Node_2",
|
|
833
|
+
sourcePort: "output",
|
|
834
|
+
targetPort: "input"
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
];
|
|
840
|
+
var EDGE_DELETE_EXAMPLES = [
|
|
841
|
+
{
|
|
842
|
+
Description: "Delete an edge by ID",
|
|
843
|
+
Command: "uip maestro flow edge delete flow.json edge_a1b2c3d4",
|
|
844
|
+
Output: {
|
|
845
|
+
Code: "EdgeDeleteSuccess",
|
|
846
|
+
Data: {
|
|
847
|
+
DeletedEdge: {
|
|
848
|
+
id: "edge_a1b2c3d4",
|
|
849
|
+
source: "Node_1",
|
|
850
|
+
target: "Node_2"
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
];
|
|
856
|
+
var EDGE_REMOVE_EXAMPLES = [
|
|
857
|
+
{
|
|
858
|
+
Description: "Remove an edge by ID",
|
|
859
|
+
Command: "uip maestro flow edge remove flow.json edge_a1b2c3d4",
|
|
860
|
+
Output: {
|
|
861
|
+
Code: "EdgeRemoveSuccess",
|
|
862
|
+
Data: {
|
|
863
|
+
DeletedEdge: {
|
|
864
|
+
id: "edge_a1b2c3d4",
|
|
865
|
+
source: "Node_1",
|
|
866
|
+
target: "Node_2"
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
];
|
|
872
|
+
function getDeleteEdgeInstructions(file) {
|
|
873
|
+
return `Check that "${file}" exists and run 'uip maestro flow edge list ${file}' to find a valid edge ID, then retry.`;
|
|
874
|
+
}
|
|
875
|
+
async function runDeleteEdge(file, edgeId, code) {
|
|
876
|
+
const { deleteEdgeFromFlow } = await import("./edge-service-e77bndbe.js");
|
|
877
|
+
const normalizedEdgeId = edgeId.trim();
|
|
878
|
+
const [error, result] = await catchError(deleteEdgeFromFlow(file, normalizedEdgeId));
|
|
879
|
+
if (error) {
|
|
880
|
+
OutputFormatter.error({
|
|
881
|
+
Result: RESULTS.Failure,
|
|
882
|
+
Message: "Error deleting edge",
|
|
883
|
+
Instructions: getDeleteEdgeInstructions(file)
|
|
884
|
+
});
|
|
885
|
+
processContext.exit(1);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
OutputFormatter.success({
|
|
889
|
+
Result: RESULTS.Success,
|
|
890
|
+
Code: code,
|
|
891
|
+
Data: {
|
|
892
|
+
DeletedEdge: result.deletedEdge
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
var registerEdgeCommand = (program) => {
|
|
897
|
+
const edge = program.command("edge").description("Manage edges in a Flow file");
|
|
898
|
+
edge.command("list").description("List all edges in a Flow file").argument("<file>", "Path to the .flow file").examples(EDGE_LIST_EXAMPLES).trackedAction(processContext, async (file) => {
|
|
899
|
+
const { listEdgesInFlow } = await import("./edge-service-e77bndbe.js");
|
|
900
|
+
const [error, result] = await catchError(listEdgesInFlow(file));
|
|
901
|
+
if (error) {
|
|
902
|
+
OutputFormatter.error({
|
|
903
|
+
Result: RESULTS.Failure,
|
|
904
|
+
Message: "Error listing edges",
|
|
905
|
+
Instructions: error.message
|
|
906
|
+
});
|
|
907
|
+
processContext.exit(1);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
OutputFormatter.success({
|
|
911
|
+
Result: RESULTS.Success,
|
|
912
|
+
Code: "EdgeListSuccess",
|
|
913
|
+
Data: {
|
|
914
|
+
Edges: result.edges,
|
|
915
|
+
Count: result.count
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
edge.command("add").description("Add an edge between two nodes in a Flow file").argument("<file>", "Path to the .flow file").argument("<source-id>", "ID of the source node").argument("<target-id>", "ID of the target node").option("--source-port <port>", 'Source port name (defaults to "output")').option("--target-port <port>", 'Target port name (defaults to "input")').examples(EDGE_ADD_EXAMPLES).trackedAction(processContext, async (file, sourceId, targetId, options) => {
|
|
920
|
+
const { addEdgeToFlow } = await import("./edge-service-e77bndbe.js");
|
|
921
|
+
const [error, result] = await catchError(addEdgeToFlow(file, sourceId, targetId, {
|
|
922
|
+
sourcePort: options.sourcePort,
|
|
923
|
+
targetPort: options.targetPort
|
|
924
|
+
}));
|
|
925
|
+
if (error) {
|
|
926
|
+
OutputFormatter.error({
|
|
927
|
+
Result: RESULTS.Failure,
|
|
928
|
+
Message: "Error adding edge",
|
|
929
|
+
Instructions: error.message
|
|
930
|
+
});
|
|
931
|
+
processContext.exit(1);
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (result.alreadyExists) {
|
|
935
|
+
const warning = `Edge already exists with id "${result.edge.id}" for (${result.edge.sourceNodeId}:${result.edge.sourcePort} -> ${result.edge.targetNodeId}:${result.edge.targetPort}). No changes written.`;
|
|
936
|
+
logger.warn(warning);
|
|
937
|
+
OutputFormatter.success({
|
|
938
|
+
Result: RESULTS.Success,
|
|
939
|
+
Code: "EdgeAlreadyExists",
|
|
940
|
+
Instructions: warning,
|
|
941
|
+
Data: {
|
|
942
|
+
Edge: result.edge
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
OutputFormatter.success({
|
|
948
|
+
Result: RESULTS.Success,
|
|
949
|
+
Code: "EdgeAddSuccess",
|
|
950
|
+
Data: {
|
|
951
|
+
Edge: result.edge
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
edge.command("delete").description(`Delete an edge from a Flow file.
|
|
956
|
+
Use 'uip maestro flow edge list' to find the edge ID.`).argument("<file>", "Path to the .flow file").argument("<edge-id>", "ID of the edge to delete").examples(EDGE_DELETE_EXAMPLES).trackedAction(processContext, async (file, edgeId) => runDeleteEdge(file, edgeId, "EdgeDeleteSuccess"));
|
|
957
|
+
edge.command("remove").description(`Remove an edge from a Flow file.
|
|
958
|
+
Alias for 'edge delete'. Use 'uip maestro flow edge list' to find the edge ID.`).argument("<file>", "Path to the .flow file").argument("<edge-id>", "ID of the edge to remove").examples(EDGE_REMOVE_EXAMPLES).trackedAction(processContext, async (file, edgeId) => runDeleteEdge(file, edgeId, "EdgeRemoveSuccess"));
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
// src/services/flow-eval-run-format.ts
|
|
962
|
+
function formatDuration(durationMs) {
|
|
963
|
+
if (durationMs === null || durationMs === undefined) {
|
|
964
|
+
return "-";
|
|
965
|
+
}
|
|
966
|
+
return `${(durationMs / 1000).toFixed(1)}s`;
|
|
967
|
+
}
|
|
968
|
+
function formatEvaluatorScores(scores, evaluators) {
|
|
969
|
+
if (!scores || scores.length === 0) {
|
|
970
|
+
return "-";
|
|
971
|
+
}
|
|
972
|
+
return scores.map((score) => {
|
|
973
|
+
const evaluator = evaluators.find((item) => item.id === score.evaluatorId);
|
|
974
|
+
return `${evaluator?.name ?? score.evaluatorId}: ${score.value}`;
|
|
975
|
+
}).join(", ");
|
|
976
|
+
}
|
|
977
|
+
function evaluatorSnapshotName(snapshot) {
|
|
978
|
+
if (typeof snapshot === "object" && snapshot !== null && !Array.isArray(snapshot) && typeof snapshot.name === "string") {
|
|
979
|
+
return snapshot.name;
|
|
980
|
+
}
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
function remoteEvaluatorName(evaluatorId, evaluatorSnapshot, evaluators) {
|
|
984
|
+
return evaluators.find((item) => item.id === evaluatorId)?.name ?? evaluatorSnapshotName(evaluatorSnapshot) ?? evaluatorId;
|
|
985
|
+
}
|
|
986
|
+
function remoteRunScore(run) {
|
|
987
|
+
if (run.result?.scores && run.result.scores.length > 0) {
|
|
988
|
+
const sum = run.result.scores.reduce((total, score) => total + score.value, 0);
|
|
989
|
+
return Number((sum / run.result.scores.length).toFixed(4));
|
|
990
|
+
}
|
|
991
|
+
const value = run.result?.score?.value;
|
|
992
|
+
return typeof value === "number" || typeof value === "boolean" ? value : "-";
|
|
993
|
+
}
|
|
994
|
+
function formatRemoteEvalRunRow(run, evaluators, verbose = false) {
|
|
995
|
+
let evaluatorScores = "-";
|
|
996
|
+
let justifications = "-";
|
|
997
|
+
if (run.evaluatorRuns && run.evaluatorRuns.length > 0) {
|
|
998
|
+
evaluatorScores = run.evaluatorRuns.map((evaluatorRun) => {
|
|
999
|
+
const name = remoteEvaluatorName(evaluatorRun.evaluatorId, evaluatorRun.evaluatorSnapshot, evaluators);
|
|
1000
|
+
const score = evaluatorRun.result?.score;
|
|
1001
|
+
if (score?.type === "error") {
|
|
1002
|
+
return `${name}: Error`;
|
|
1003
|
+
}
|
|
1004
|
+
return `${name}: ${score?.value ?? "-"}`;
|
|
1005
|
+
}).join(", ");
|
|
1006
|
+
const justificationParts = run.evaluatorRuns.filter((evaluatorRun) => evaluatorRun.result?.score?.justification).map((evaluatorRun) => {
|
|
1007
|
+
const name = remoteEvaluatorName(evaluatorRun.evaluatorId, evaluatorRun.evaluatorSnapshot, evaluators);
|
|
1008
|
+
return `[${name}] ${evaluatorRun.result?.score?.justification}`;
|
|
1009
|
+
});
|
|
1010
|
+
if (justificationParts.length > 0) {
|
|
1011
|
+
justifications = justificationParts.join(" | ");
|
|
1012
|
+
}
|
|
1013
|
+
} else if (run.result?.scores && run.result.scores.length > 0) {
|
|
1014
|
+
evaluatorScores = formatEvaluatorScores(run.result.scores, evaluators);
|
|
1015
|
+
}
|
|
1016
|
+
const tokens = run.completionMetrics?.tokens;
|
|
1017
|
+
const duration = run.completionMetrics?.duration;
|
|
1018
|
+
const error = run.errorMessage ?? (typeof run.error === "string" ? run.error : run.error?.message) ?? "-";
|
|
1019
|
+
const row = {
|
|
1020
|
+
DataPoint: run.evalSnapshot?.name ?? run.id,
|
|
1021
|
+
Status: run.status,
|
|
1022
|
+
Score: remoteRunScore(run),
|
|
1023
|
+
EvaluatorScores: evaluatorScores,
|
|
1024
|
+
Tokens: typeof tokens === "number" && tokens > 0 ? tokens : "-",
|
|
1025
|
+
Duration: typeof duration === "number" ? formatDuration(duration) : "-",
|
|
1026
|
+
Error: error
|
|
1027
|
+
};
|
|
1028
|
+
if (verbose) {
|
|
1029
|
+
row.Justifications = justifications;
|
|
1030
|
+
}
|
|
1031
|
+
return row;
|
|
1032
|
+
}
|
|
1033
|
+
function isFailedRemoteRun(run) {
|
|
1034
|
+
const status = run.status.toLowerCase();
|
|
1035
|
+
if (status === "failed" || status === "workloadfailed" || run.errorMessage || run.error) {
|
|
1036
|
+
return true;
|
|
1037
|
+
}
|
|
1038
|
+
if (run.result?.score?.type === "error") {
|
|
1039
|
+
return true;
|
|
1040
|
+
}
|
|
1041
|
+
return run.evaluatorRuns?.some((evaluatorRun) => evaluatorRun.result?.score?.type === "error" || evaluatorRun.result?.score?.value === false) ?? false;
|
|
1042
|
+
}
|
|
1043
|
+
function isTerminalRemoteStatus(status) {
|
|
1044
|
+
return ["completed", "partiallycompleted", "failed"].includes(status.toLowerCase());
|
|
1045
|
+
}
|
|
1046
|
+
function formatEvalSetRunRow(run, evaluators) {
|
|
1047
|
+
return {
|
|
1048
|
+
EvalSetRunId: run.id,
|
|
1049
|
+
Status: run.status,
|
|
1050
|
+
Score: run.score ?? "-",
|
|
1051
|
+
DataPoints: run.numberOfEvalsExecuted ?? "-",
|
|
1052
|
+
Duration: formatDuration(run.durationMilliseconds),
|
|
1053
|
+
EvaluatorScores: formatEvaluatorScores(run.evaluatorScores, evaluators),
|
|
1054
|
+
CreatedAt: run.createdAt
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/commands/eval-run.ts
|
|
1059
|
+
var FLOW_EVAL_PATH_OPTION_DESCRIPTION = "Path to a Flow project directory, or a solution directory with exactly one Flow project";
|
|
1060
|
+
var POLL_INTERVAL_MS = 5000;
|
|
1061
|
+
var POLL_TIMEOUT_MS = 600000;
|
|
1062
|
+
var FLOW_EVAL_RUN_START_EXAMPLES = [
|
|
1063
|
+
{
|
|
1064
|
+
Description: "Start a Studio Web Flow evaluation run",
|
|
1065
|
+
Command: 'uip maestro flow eval run start --set "Default Evaluation Set" --path ./my-solution/Flow',
|
|
1066
|
+
Output: {
|
|
1067
|
+
Code: "FlowEvalRunStarted",
|
|
1068
|
+
Data: {
|
|
1069
|
+
EvalSetRunId: "a1b2c3d4-0000-0000-0000-000000000101",
|
|
1070
|
+
EvalSetName: "Default Evaluation Set",
|
|
1071
|
+
DataPoints: 3,
|
|
1072
|
+
Evaluators: 2
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
];
|
|
1077
|
+
var FLOW_EVAL_RUN_STATUS_EXAMPLES = [
|
|
1078
|
+
{
|
|
1079
|
+
Description: "Check the status of a Studio Web Flow evaluation run",
|
|
1080
|
+
Command: 'uip maestro flow eval run status a1b2c3d4-0000-0000-0000-000000000101 --set "Default Evaluation Set" --path ./my-solution/Flow',
|
|
1081
|
+
Output: {
|
|
1082
|
+
Code: "FlowEvalRunStatus",
|
|
1083
|
+
Data: {
|
|
1084
|
+
EvalSetRunId: "a1b2c3d4-0000-0000-0000-000000000101",
|
|
1085
|
+
Status: "Completed",
|
|
1086
|
+
Score: 0.86,
|
|
1087
|
+
Duration: "42.5s"
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
];
|
|
1092
|
+
var FLOW_EVAL_RUN_RESULTS_EXAMPLES = [
|
|
1093
|
+
{
|
|
1094
|
+
Description: "Show detailed per-data-point results for a Flow run",
|
|
1095
|
+
Command: 'uip maestro flow eval run results a1b2c3d4-0000-0000-0000-000000000101 --set "Default Evaluation Set" --path ./my-solution/Flow',
|
|
1096
|
+
Output: {
|
|
1097
|
+
Code: "FlowEvalRunResults",
|
|
1098
|
+
Data: [
|
|
1099
|
+
{
|
|
1100
|
+
DataPoint: "simple-greeting",
|
|
1101
|
+
Status: "completed",
|
|
1102
|
+
EvaluatorScores: "Exact: true",
|
|
1103
|
+
Duration: "1.8s",
|
|
1104
|
+
Error: "-"
|
|
1105
|
+
}
|
|
1106
|
+
]
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
];
|
|
1110
|
+
var FLOW_EVAL_RUN_LIST_EXAMPLES = [
|
|
1111
|
+
{
|
|
1112
|
+
Description: "List Studio Web evaluation runs for a Flow eval set",
|
|
1113
|
+
Command: 'uip maestro flow eval run list --set "Default Evaluation Set" --path ./my-solution/Flow',
|
|
1114
|
+
Output: {
|
|
1115
|
+
Code: "FlowEvalRunList",
|
|
1116
|
+
Data: [
|
|
1117
|
+
{
|
|
1118
|
+
EvalSetRunId: "a1b2c3d4-0000-0000-0000-000000000101",
|
|
1119
|
+
Status: "Completed",
|
|
1120
|
+
Score: 0.86,
|
|
1121
|
+
DataPoints: 3,
|
|
1122
|
+
Duration: "42.5s",
|
|
1123
|
+
CreatedAt: "2026-04-15T10:30:00Z"
|
|
1124
|
+
}
|
|
1125
|
+
]
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
];
|
|
1129
|
+
var FLOW_EVAL_RUN_COMPARE_EXAMPLES = [
|
|
1130
|
+
{
|
|
1131
|
+
Description: "Compare two Studio Web Flow evaluation runs",
|
|
1132
|
+
Command: 'uip maestro flow eval run compare a1b2c3d4-0000-0000-0000-000000000101 --compare-to a1b2c3d4-0000-0000-0000-000000000102 --set "Default Evaluation Set" --path ./my-solution/Flow',
|
|
1133
|
+
Output: {
|
|
1134
|
+
Code: "FlowEvalRunComparison",
|
|
1135
|
+
Data: {
|
|
1136
|
+
RunA: {
|
|
1137
|
+
Id: "a1b2c3d4-0000-0000-0000-000000000101",
|
|
1138
|
+
Score: 0.86,
|
|
1139
|
+
Status: "Completed"
|
|
1140
|
+
},
|
|
1141
|
+
RunB: {
|
|
1142
|
+
Id: "a1b2c3d4-0000-0000-0000-000000000102",
|
|
1143
|
+
Score: 0.8,
|
|
1144
|
+
Status: "Completed"
|
|
1145
|
+
},
|
|
1146
|
+
ScoreDelta: 0.06
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
];
|
|
1151
|
+
function addTargetOptions(command) {
|
|
1152
|
+
return command.requiredOption("--set <name>", "Evaluation set name or ID").option("--solution-id <id>", "Solution ID from Studio Web").option("--project-id <id>", "Flow project ID from Studio Web (reads from parent .uipx or SolutionStorage.json if not provided)").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION, ".");
|
|
1153
|
+
}
|
|
1154
|
+
function addStartOptions(command) {
|
|
1155
|
+
return addTargetOptions(command).option("--entry-point <entry>", "Flow entry point path (/Main.bpmn#start) or start node ID").option("--folder-key <key>", "Orchestrator folder key. Uses the personal workspace if not provided.").option("--debug-mode <mode>", "Studio Web debug mode override", (value) => Number.parseInt(value, 10));
|
|
1156
|
+
}
|
|
1157
|
+
function outputError(message, error) {
|
|
1158
|
+
OutputFormatter.error({
|
|
1159
|
+
Result: RESULTS.Failure,
|
|
1160
|
+
Message: message,
|
|
1161
|
+
Instructions: error.message
|
|
1162
|
+
});
|
|
1163
|
+
processContext.exit(1);
|
|
1164
|
+
}
|
|
1165
|
+
async function resolveStudioWebConfig() {
|
|
1166
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
1167
|
+
const [loginError, login] = await catchError(getLoginStatusAsync());
|
|
1168
|
+
if (loginError) {
|
|
1169
|
+
throw new Error(loginError.message);
|
|
1170
|
+
}
|
|
1171
|
+
if (login.loginStatus !== "Logged in" || !login.baseUrl) {
|
|
1172
|
+
throw new Error("Not logged in. Run 'uip login' first.");
|
|
1173
|
+
}
|
|
1174
|
+
if (!login.organizationId) {
|
|
1175
|
+
throw new Error("Organization ID not available from login.");
|
|
1176
|
+
}
|
|
1177
|
+
if (!login.tenantId) {
|
|
1178
|
+
throw new Error("Tenant ID not available from login.");
|
|
1179
|
+
}
|
|
1180
|
+
if (!login.tenantName) {
|
|
1181
|
+
throw new Error("Tenant name not available from login.");
|
|
1182
|
+
}
|
|
1183
|
+
if (!login.accessToken) {
|
|
1184
|
+
throw new Error("Access token not available from login.");
|
|
1185
|
+
}
|
|
1186
|
+
if (!login.organizationName) {
|
|
1187
|
+
throw new Error("Organization name not available from login. Required for Studio Web evaluation.");
|
|
1188
|
+
}
|
|
1189
|
+
return {
|
|
1190
|
+
baseUrl: login.baseUrl,
|
|
1191
|
+
organizationId: login.organizationId,
|
|
1192
|
+
tenantId: login.tenantId,
|
|
1193
|
+
tenantName: login.tenantName,
|
|
1194
|
+
authToken: login.accessToken,
|
|
1195
|
+
organizationName: login.organizationName
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
async function pollRemoteEvalSetRun(service, config, workloadId, evalSetRunId, timeoutMs) {
|
|
1199
|
+
const start = Date.now();
|
|
1200
|
+
while (Date.now() - start < timeoutMs) {
|
|
1201
|
+
const [error, run] = await catchError(service.getRemoteEvalSetRun(config, workloadId, evalSetRunId));
|
|
1202
|
+
if (error) {
|
|
1203
|
+
OutputFormatter.log({
|
|
1204
|
+
Message: `Polling error: ${error.message}. Retrying...`
|
|
1205
|
+
});
|
|
1206
|
+
} else if (run) {
|
|
1207
|
+
if (isTerminalRemoteStatus(run.status)) {
|
|
1208
|
+
return run;
|
|
1209
|
+
}
|
|
1210
|
+
OutputFormatter.log({
|
|
1211
|
+
Message: `Status: ${run.status}... (${Math.round((Date.now() - start) / 1000)}s)`
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
1215
|
+
}
|
|
1216
|
+
throw new Error(`Evaluation timed out after ${timeoutMs / 1000} seconds`);
|
|
1217
|
+
}
|
|
1218
|
+
async function outputCompletedRemoteRun(service, config, projectPath, startResult, timeoutSeconds) {
|
|
1219
|
+
const timeoutMs = timeoutSeconds !== undefined ? timeoutSeconds * 1000 : POLL_TIMEOUT_MS;
|
|
1220
|
+
const finalRun = await pollRemoteEvalSetRun(service, config, startResult.context.workloadId, startResult.evalSetRunId, timeoutMs);
|
|
1221
|
+
const [evalRuns, evaluators] = await Promise.all([
|
|
1222
|
+
service.getRemoteEvalRuns(config, startResult.context.workloadId, startResult.evalSetRunId),
|
|
1223
|
+
service.listEvaluators(projectPath)
|
|
1224
|
+
]);
|
|
1225
|
+
OutputFormatter.success({
|
|
1226
|
+
Result: RESULTS.Success,
|
|
1227
|
+
Code: "FlowEvalRunCompleted",
|
|
1228
|
+
Data: {
|
|
1229
|
+
Status: finalRun.status,
|
|
1230
|
+
Score: finalRun.score ?? "-",
|
|
1231
|
+
Duration: formatDuration(finalRun.durationMilliseconds),
|
|
1232
|
+
EvaluatorScores: formatEvaluatorScores(finalRun.evaluatorScores, evaluators),
|
|
1233
|
+
DataPoints: evalRuns.length
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
if (evalRuns.length > 0) {
|
|
1237
|
+
OutputFormatter.success({
|
|
1238
|
+
Result: RESULTS.Success,
|
|
1239
|
+
Code: "FlowEvalRunResults",
|
|
1240
|
+
Data: evalRuns.map((run) => formatRemoteEvalRunRow(run, evaluators))
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
async function exportRows(exportFormat, rows) {
|
|
1245
|
+
const fs = getFileSystem();
|
|
1246
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1247
|
+
if (exportFormat === "json") {
|
|
1248
|
+
const filePath = fs.path.resolve(`flow-eval-results-${timestamp}.json`);
|
|
1249
|
+
await fs.writeFile(filePath, JSON.stringify(rows, null, 2));
|
|
1250
|
+
OutputFormatter.success({
|
|
1251
|
+
Result: RESULTS.Success,
|
|
1252
|
+
Code: "FlowEvalRunExported",
|
|
1253
|
+
Data: { Format: "json", File: filePath, Records: rows.length }
|
|
1254
|
+
});
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if (exportFormat === "csv") {
|
|
1258
|
+
const filePath = fs.path.resolve(`flow-eval-results-${timestamp}.csv`);
|
|
1259
|
+
const headers = Object.keys(rows[0] ?? {});
|
|
1260
|
+
const csvRows = [
|
|
1261
|
+
headers.join(","),
|
|
1262
|
+
...rows.map((row) => headers.map((header) => {
|
|
1263
|
+
const value = String(row[header] ?? "");
|
|
1264
|
+
return value.includes(",") || value.includes('"') || value.includes(`
|
|
1265
|
+
`) ? `"${value.replace(/"/g, '""')}"` : value;
|
|
1266
|
+
}).join(","))
|
|
1267
|
+
];
|
|
1268
|
+
await fs.writeFile(filePath, csvRows.join(`
|
|
1269
|
+
`));
|
|
1270
|
+
OutputFormatter.success({
|
|
1271
|
+
Result: RESULTS.Success,
|
|
1272
|
+
Code: "FlowEvalRunExported",
|
|
1273
|
+
Data: { Format: "csv", File: filePath, Records: rows.length }
|
|
1274
|
+
});
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
throw new Error(`Unknown export format "${exportFormat}". Use "json" or "csv".`);
|
|
1278
|
+
}
|
|
1279
|
+
async function resolveRemoteTarget(service, options, config, requireSolutionId = false) {
|
|
1280
|
+
return service.resolveRemoteEvalTarget(options.path, options.set, {
|
|
1281
|
+
solutionId: options.solutionId,
|
|
1282
|
+
projectId: options.projectId
|
|
1283
|
+
}, requireSolutionId, config);
|
|
1284
|
+
}
|
|
1285
|
+
function registerEvalRunCommand(evalCmd) {
|
|
1286
|
+
const runCmd = evalCmd.command("run").description("Run Flow evaluation sets in Studio Web");
|
|
1287
|
+
addStartOptions(runCmd.command("start").description("Start a Studio Web evaluation run (Flow solution must be available in Studio Web)")).option("--wait", "Wait for the evaluation to complete and show results").option("--timeout <seconds>", "Timeout in seconds for --wait polling", (value) => Number.parseInt(value, 10)).examples(FLOW_EVAL_RUN_START_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1288
|
+
const [error] = await catchError((async () => {
|
|
1289
|
+
const config = await resolveStudioWebConfig();
|
|
1290
|
+
const { FlowEvalRunService } = await import("./flow-eval-run-service-01kdky72.js");
|
|
1291
|
+
const service = new FlowEvalRunService;
|
|
1292
|
+
const result = await service.startRemoteEvalRun(options.path, options.set, config, {
|
|
1293
|
+
solutionId: options.solutionId,
|
|
1294
|
+
projectId: options.projectId,
|
|
1295
|
+
entryPoint: options.entryPoint,
|
|
1296
|
+
folderKey: options.folderKey,
|
|
1297
|
+
debugMode: options.debugMode
|
|
1298
|
+
});
|
|
1299
|
+
OutputFormatter.success({
|
|
1300
|
+
Result: RESULTS.Success,
|
|
1301
|
+
Code: "FlowEvalRunStarted",
|
|
1302
|
+
Data: {
|
|
1303
|
+
EvalSetRunId: result.evalSetRunId,
|
|
1304
|
+
EvalSetName: result.evalSetName,
|
|
1305
|
+
DataPoints: result.dataPoints,
|
|
1306
|
+
Evaluators: result.evaluators,
|
|
1307
|
+
Status: result.status
|
|
1308
|
+
},
|
|
1309
|
+
Instructions: options.wait ? "Waiting for completion..." : `Monitor with: uip maestro flow eval run status ${result.evalSetRunId} --set "${options.set}" --path ${options.path}`
|
|
1310
|
+
});
|
|
1311
|
+
if (options.wait) {
|
|
1312
|
+
await outputCompletedRemoteRun(service, config, options.path, result, options.timeout);
|
|
1313
|
+
}
|
|
1314
|
+
})());
|
|
1315
|
+
if (error) {
|
|
1316
|
+
outputError("Failed to start flow evaluation run", error);
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
addTargetOptions(runCmd.command("status").description("Check the status of a Studio Web Flow evaluation run").argument("<evalSetRunId>", "Evaluation set run ID")).examples(FLOW_EVAL_RUN_STATUS_EXAMPLES).trackedAction(processContext, async (evalSetRunId, options) => {
|
|
1320
|
+
const [error] = await catchError((async () => {
|
|
1321
|
+
const config = await resolveStudioWebConfig();
|
|
1322
|
+
const { FlowEvalRunService } = await import("./flow-eval-run-service-01kdky72.js");
|
|
1323
|
+
const service = new FlowEvalRunService;
|
|
1324
|
+
const target = await resolveRemoteTarget(service, options, config);
|
|
1325
|
+
const [run, evaluators] = await Promise.all([
|
|
1326
|
+
service.getRemoteEvalSetRun(config, target.workloadId, evalSetRunId),
|
|
1327
|
+
service.listEvaluators(target.projectPath)
|
|
1328
|
+
]);
|
|
1329
|
+
OutputFormatter.success({
|
|
1330
|
+
Result: RESULTS.Success,
|
|
1331
|
+
Code: "FlowEvalRunStatus",
|
|
1332
|
+
Data: {
|
|
1333
|
+
EvalSetRunId: run.id,
|
|
1334
|
+
Status: run.status,
|
|
1335
|
+
Score: run.score ?? "-",
|
|
1336
|
+
Duration: formatDuration(run.durationMilliseconds),
|
|
1337
|
+
EvaluatorScores: formatEvaluatorScores(run.evaluatorScores, evaluators)
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
})());
|
|
1341
|
+
if (error) {
|
|
1342
|
+
outputError("Failed to get flow evaluation run status", error);
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
addTargetOptions(runCmd.command("results").description("Get detailed results for a Studio Web Flow evaluation run").argument("<evalSetRunId>", "Evaluation set run ID")).option("--only-failed", "Show only failed or errored data points").option("--verbose", "Include evaluator justifications in output").option("--export-format <json|csv>", "Export results to file (json or csv)").examples(FLOW_EVAL_RUN_RESULTS_EXAMPLES).trackedAction(processContext, async (evalSetRunId, options) => {
|
|
1346
|
+
const [error] = await catchError((async () => {
|
|
1347
|
+
const config = await resolveStudioWebConfig();
|
|
1348
|
+
const { FlowEvalRunService } = await import("./flow-eval-run-service-01kdky72.js");
|
|
1349
|
+
const service = new FlowEvalRunService;
|
|
1350
|
+
const target = await resolveRemoteTarget(service, options, config);
|
|
1351
|
+
let evalRuns = await service.getRemoteEvalRuns(config, target.workloadId, evalSetRunId);
|
|
1352
|
+
if (options.onlyFailed) {
|
|
1353
|
+
evalRuns = evalRuns.filter(isFailedRemoteRun);
|
|
1354
|
+
}
|
|
1355
|
+
if (evalRuns.length === 0) {
|
|
1356
|
+
OutputFormatter.success({
|
|
1357
|
+
Result: RESULTS.Success,
|
|
1358
|
+
Code: "FlowEvalRunResults",
|
|
1359
|
+
Data: []
|
|
1360
|
+
});
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
const evaluators = await service.listEvaluators(target.projectPath);
|
|
1364
|
+
const rows = evalRuns.map((run) => formatRemoteEvalRunRow(run, evaluators, options.verbose));
|
|
1365
|
+
if (options.exportFormat) {
|
|
1366
|
+
await exportRows(options.exportFormat, rows);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
OutputFormatter.success({
|
|
1370
|
+
Result: RESULTS.Success,
|
|
1371
|
+
Code: "FlowEvalRunResults",
|
|
1372
|
+
Data: rows
|
|
1373
|
+
});
|
|
1374
|
+
})());
|
|
1375
|
+
if (error) {
|
|
1376
|
+
outputError("Failed to get flow evaluation results", error);
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
addTargetOptions(runCmd.command("list").description("List Studio Web evaluation runs for a Flow eval set")).examples(FLOW_EVAL_RUN_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1380
|
+
const [error] = await catchError((async () => {
|
|
1381
|
+
const config = await resolveStudioWebConfig();
|
|
1382
|
+
const { FlowEvalRunService } = await import("./flow-eval-run-service-01kdky72.js");
|
|
1383
|
+
const service = new FlowEvalRunService;
|
|
1384
|
+
const target = await resolveRemoteTarget(service, options, config);
|
|
1385
|
+
const runs = await service.listRemoteEvalSetRuns(config, target.workloadId, target.evalSet.id);
|
|
1386
|
+
if (runs.length === 0) {
|
|
1387
|
+
OutputFormatter.success({
|
|
1388
|
+
Result: RESULTS.Success,
|
|
1389
|
+
Code: "FlowEvalRunList",
|
|
1390
|
+
Data: []
|
|
1391
|
+
});
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
const evaluators = await service.listEvaluators(target.projectPath);
|
|
1395
|
+
OutputFormatter.success({
|
|
1396
|
+
Result: RESULTS.Success,
|
|
1397
|
+
Code: "FlowEvalRunList",
|
|
1398
|
+
Data: runs.map((run) => formatEvalSetRunRow(run, evaluators))
|
|
1399
|
+
});
|
|
1400
|
+
})());
|
|
1401
|
+
if (error) {
|
|
1402
|
+
outputError("Failed to list flow evaluation runs", error);
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
addTargetOptions(runCmd.command("compare").description("Compare results of two Studio Web Flow evaluation runs").argument("<evalSetRunId>", "First evaluation set run ID").requiredOption("--compare-to <id>", "Second evaluation set run ID to compare against")).examples(FLOW_EVAL_RUN_COMPARE_EXAMPLES).trackedAction(processContext, async (evalSetRunId, options) => {
|
|
1406
|
+
const [error] = await catchError((async () => {
|
|
1407
|
+
const config = await resolveStudioWebConfig();
|
|
1408
|
+
const { FlowEvalRunService } = await import("./flow-eval-run-service-01kdky72.js");
|
|
1409
|
+
const service = new FlowEvalRunService;
|
|
1410
|
+
const target = await resolveRemoteTarget(service, options, config);
|
|
1411
|
+
const [runsA, runsB, runMetaA, runMetaB] = await Promise.all([
|
|
1412
|
+
service.getRemoteEvalRuns(config, target.workloadId, evalSetRunId),
|
|
1413
|
+
service.getRemoteEvalRuns(config, target.workloadId, options.compareTo),
|
|
1414
|
+
service.getRemoteEvalSetRun(config, target.workloadId, evalSetRunId),
|
|
1415
|
+
service.getRemoteEvalSetRun(config, target.workloadId, options.compareTo)
|
|
1416
|
+
]);
|
|
1417
|
+
OutputFormatter.success({
|
|
1418
|
+
Result: RESULTS.Success,
|
|
1419
|
+
Code: "FlowEvalRunComparison",
|
|
1420
|
+
Data: buildComparison(evalSetRunId, options.compareTo, runMetaA, runMetaB, runsA, runsB)
|
|
1421
|
+
});
|
|
1422
|
+
})());
|
|
1423
|
+
if (error) {
|
|
1424
|
+
outputError("Failed to compare flow evaluation runs", error);
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
function buildComparison(evalSetRunId, compareTo, runMetaA, runMetaB, runsA, runsB) {
|
|
1429
|
+
const runsBByName = new Map;
|
|
1430
|
+
for (const run of runsB) {
|
|
1431
|
+
runsBByName.set(run.evalSnapshot?.name ?? run.id, run);
|
|
1432
|
+
}
|
|
1433
|
+
const dataPoints = runsA.map((runA) => {
|
|
1434
|
+
const name = runA.evalSnapshot?.name ?? runA.id;
|
|
1435
|
+
const runB = runsBByName.get(name);
|
|
1436
|
+
const scoreA = remoteRunScore(runA);
|
|
1437
|
+
const scoreB = runB ? remoteRunScore(runB) : "-";
|
|
1438
|
+
let delta = "-";
|
|
1439
|
+
if (typeof scoreA === "number" && typeof scoreB === "number") {
|
|
1440
|
+
delta = Number((scoreA - scoreB).toFixed(4));
|
|
1441
|
+
}
|
|
1442
|
+
return {
|
|
1443
|
+
DataPoint: name,
|
|
1444
|
+
ScoreA: scoreA,
|
|
1445
|
+
ScoreB: scoreB,
|
|
1446
|
+
Delta: delta,
|
|
1447
|
+
StatusA: runA.status,
|
|
1448
|
+
StatusB: runB?.status ?? "-"
|
|
1449
|
+
};
|
|
1450
|
+
});
|
|
1451
|
+
return {
|
|
1452
|
+
RunA: {
|
|
1453
|
+
Id: evalSetRunId,
|
|
1454
|
+
Score: runMetaA.score ?? "-",
|
|
1455
|
+
Status: runMetaA.status
|
|
1456
|
+
},
|
|
1457
|
+
RunB: {
|
|
1458
|
+
Id: compareTo,
|
|
1459
|
+
Score: runMetaB.score ?? "-",
|
|
1460
|
+
Status: runMetaB.status
|
|
1461
|
+
},
|
|
1462
|
+
ScoreDelta: typeof runMetaA.score === "number" && typeof runMetaB.score === "number" ? Number((runMetaA.score - runMetaB.score).toFixed(4)) : "-",
|
|
1463
|
+
DataPoints: dataPoints
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// src/commands/eval.ts
|
|
1468
|
+
var FLOW_EVAL_PATH_OPTION_DESCRIPTION2 = "Path to a Flow project directory, or a solution directory with exactly one Flow project";
|
|
1469
|
+
function parseJsonValue(raw, label) {
|
|
1470
|
+
try {
|
|
1471
|
+
return JSON.parse(raw);
|
|
1472
|
+
} catch (err) {
|
|
1473
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1474
|
+
throw new Error(`Invalid JSON for ${label}: ${message}`);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
function parseJsonObject(raw, label) {
|
|
1478
|
+
const parsed = JSON.parse(raw);
|
|
1479
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1480
|
+
throw new Error(`${label} must be a JSON object`);
|
|
1481
|
+
}
|
|
1482
|
+
return parsed;
|
|
1483
|
+
}
|
|
1484
|
+
function parseCriteriaOption(raw) {
|
|
1485
|
+
const parsed = parseJsonObject(raw, "--criteria");
|
|
1486
|
+
const criteria = {};
|
|
1487
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1488
|
+
if (value === null || value === undefined) {
|
|
1489
|
+
criteria[key] = value;
|
|
1490
|
+
} else if (typeof value === "object" && !Array.isArray(value)) {
|
|
1491
|
+
criteria[key] = value;
|
|
1492
|
+
} else {
|
|
1493
|
+
throw new Error(`--criteria value for "${key}" must be a JSON object or null`);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return criteria;
|
|
1497
|
+
}
|
|
1498
|
+
function parseCommaSeparated(value) {
|
|
1499
|
+
if (value === undefined) {
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
1503
|
+
}
|
|
1504
|
+
function collectInputFile(value, previous) {
|
|
1505
|
+
return [...previous, value];
|
|
1506
|
+
}
|
|
1507
|
+
function parseInputFiles(raw) {
|
|
1508
|
+
return raw.map((entry) => {
|
|
1509
|
+
const eq = entry.indexOf("=");
|
|
1510
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
1511
|
+
throw new Error(`--input-file must be "<key>=<path>" (got "${entry}")`);
|
|
1512
|
+
}
|
|
1513
|
+
const key = entry.slice(0, eq).trim();
|
|
1514
|
+
const sourcePath = entry.slice(eq + 1).trim();
|
|
1515
|
+
if (!key || !sourcePath) {
|
|
1516
|
+
throw new Error(`--input-file must be "<key>=<path>" (got "${entry}")`);
|
|
1517
|
+
}
|
|
1518
|
+
return { key, sourcePath };
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
function outputError2(message, error) {
|
|
1522
|
+
OutputFormatter.error({
|
|
1523
|
+
Result: RESULTS.Failure,
|
|
1524
|
+
Message: message,
|
|
1525
|
+
Instructions: error.message
|
|
1526
|
+
});
|
|
1527
|
+
processContext.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
function registerEvalCommand(program) {
|
|
1530
|
+
const evalCmd = program.command("eval").description("Manage and run Flow evaluation sets");
|
|
1531
|
+
registerEvaluatorCommands(evalCmd);
|
|
1532
|
+
registerEvalSetCommands(evalCmd);
|
|
1533
|
+
registerEvaluationCommands(evalCmd);
|
|
1534
|
+
registerSimulationCommands(evalCmd);
|
|
1535
|
+
registerEvalRunCommand(evalCmd);
|
|
1536
|
+
}
|
|
1537
|
+
function registerEvaluatorCommands(evalCmd) {
|
|
1538
|
+
const evaluator = evalCmd.command("evaluator").description("Manage Flow evaluators");
|
|
1539
|
+
evaluator.command("add").description("Add an evaluator to the flow project").argument("<name>", "Evaluator name").requiredOption("--type <type>", `Evaluator type: ${evaluatorTypeAliases().join(", ")}`).option("--description <text>", "Evaluator description").option("--target-key <key>", "Target output key", "*").option("--model <model>", "LLM model for LLM judge evaluators").option("--prompt <prompt>", "Custom LLM judge prompt").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (name, options) => {
|
|
1540
|
+
const [error] = await catchError((async () => {
|
|
1541
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1542
|
+
const service = new FlowEvalService;
|
|
1543
|
+
const created = await service.addEvaluator(options.path, {
|
|
1544
|
+
name,
|
|
1545
|
+
type: options.type,
|
|
1546
|
+
description: options.description,
|
|
1547
|
+
targetOutputKey: options.targetKey,
|
|
1548
|
+
model: options.model,
|
|
1549
|
+
prompt: options.prompt
|
|
1550
|
+
});
|
|
1551
|
+
OutputFormatter.success({
|
|
1552
|
+
Result: RESULTS.Success,
|
|
1553
|
+
Code: "FlowEvalEvaluatorAdd",
|
|
1554
|
+
Data: {
|
|
1555
|
+
Name: created.name,
|
|
1556
|
+
Id: created.id,
|
|
1557
|
+
Type: created.evaluatorTypeId,
|
|
1558
|
+
File: created.fileName
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
})());
|
|
1562
|
+
if (error) {
|
|
1563
|
+
outputError2("Failed to add evaluator", error);
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
evaluator.command("list").description("List evaluators in a flow project").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (options) => {
|
|
1567
|
+
const [error] = await catchError((async () => {
|
|
1568
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1569
|
+
const service = new FlowEvalService;
|
|
1570
|
+
const evaluators = await service.listEvaluators(options.path);
|
|
1571
|
+
OutputFormatter.success({
|
|
1572
|
+
Result: RESULTS.Success,
|
|
1573
|
+
Code: "FlowEvalEvaluatorList",
|
|
1574
|
+
Data: evaluators.map((item) => ({
|
|
1575
|
+
Name: item.name ?? String(item.evaluatorConfig.name ?? ""),
|
|
1576
|
+
Id: item.id,
|
|
1577
|
+
Type: item.evaluatorTypeId,
|
|
1578
|
+
TargetKey: item.evaluatorConfig.targetOutputKey ?? "*",
|
|
1579
|
+
File: item.fileName
|
|
1580
|
+
}))
|
|
1581
|
+
});
|
|
1582
|
+
})());
|
|
1583
|
+
if (error) {
|
|
1584
|
+
outputError2("Failed to list evaluators", error);
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
evaluator.command("remove").description("Remove an evaluator from a flow project").argument("<id>", "Evaluator ID, name, or file base name").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (id, options) => {
|
|
1588
|
+
const [error] = await catchError((async () => {
|
|
1589
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1590
|
+
const service = new FlowEvalService;
|
|
1591
|
+
const removed = await service.removeEvaluator(options.path, id);
|
|
1592
|
+
if (!removed) {
|
|
1593
|
+
throw new Error(`Evaluator "${id}" not found`);
|
|
1594
|
+
}
|
|
1595
|
+
OutputFormatter.success({
|
|
1596
|
+
Result: RESULTS.Success,
|
|
1597
|
+
Code: "FlowEvalEvaluatorRemove",
|
|
1598
|
+
Data: { Status: "Evaluator removed", Id: id }
|
|
1599
|
+
});
|
|
1600
|
+
})());
|
|
1601
|
+
if (error) {
|
|
1602
|
+
outputError2("Failed to remove evaluator", error);
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
function registerEvalSetCommands(evalCmd) {
|
|
1607
|
+
const set = evalCmd.command("set").description("Manage evaluation sets");
|
|
1608
|
+
set.command("add").description("Create an evaluation set").argument("<name>", "Evaluation set name").option("--evaluators <refs>", "Comma-separated evaluator IDs or file base names (default: all)").option("--entry-point <id>", "Entry point node id stored as the eval set's selectedEntrypoint. An agent node id creates a node-scoped eval set (evaluates that node in isolation)").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (name, options) => {
|
|
1609
|
+
const [error] = await catchError((async () => {
|
|
1610
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1611
|
+
const { readEvalSetScope, readNodeTargetId } = await import("./flow-eval-node-target-c6wea7rz.js");
|
|
1612
|
+
const service = new FlowEvalService;
|
|
1613
|
+
const created = await service.addEvalSet(options.path, {
|
|
1614
|
+
name,
|
|
1615
|
+
evaluatorRefs: parseCommaSeparated(options.evaluators),
|
|
1616
|
+
entryPoint: options.entryPoint
|
|
1617
|
+
});
|
|
1618
|
+
const targetNode = readNodeTargetId(created);
|
|
1619
|
+
OutputFormatter.success({
|
|
1620
|
+
Result: RESULTS.Success,
|
|
1621
|
+
Code: "FlowEvalSetAdd",
|
|
1622
|
+
Data: {
|
|
1623
|
+
Name: created.name,
|
|
1624
|
+
Id: created.id,
|
|
1625
|
+
Evaluators: created.evaluatorRefs.length,
|
|
1626
|
+
File: created.fileName,
|
|
1627
|
+
Scope: readEvalSetScope(created),
|
|
1628
|
+
...targetNode !== undefined ? { TargetNode: targetNode } : {}
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
})());
|
|
1632
|
+
if (error) {
|
|
1633
|
+
outputError2("Failed to create evaluation set", error);
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1636
|
+
set.command("list").description("List evaluation sets").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (options) => {
|
|
1637
|
+
const [error] = await catchError((async () => {
|
|
1638
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1639
|
+
const { readEvalSetScope, readNodeTargetId } = await import("./flow-eval-node-target-c6wea7rz.js");
|
|
1640
|
+
const service = new FlowEvalService;
|
|
1641
|
+
const sets = await service.listEvalSets(options.path);
|
|
1642
|
+
OutputFormatter.success({
|
|
1643
|
+
Result: RESULTS.Success,
|
|
1644
|
+
Code: "FlowEvalSetList",
|
|
1645
|
+
Data: sets.map((item) => {
|
|
1646
|
+
const targetNode = readNodeTargetId(item);
|
|
1647
|
+
return {
|
|
1648
|
+
Name: item.name,
|
|
1649
|
+
Id: item.id,
|
|
1650
|
+
DataPoints: item.evaluations.length,
|
|
1651
|
+
Evaluators: item.evaluatorRefs.length,
|
|
1652
|
+
File: item.fileName,
|
|
1653
|
+
Scope: readEvalSetScope(item),
|
|
1654
|
+
...targetNode !== undefined ? { TargetNode: targetNode } : {}
|
|
1655
|
+
};
|
|
1656
|
+
})
|
|
1657
|
+
});
|
|
1658
|
+
})());
|
|
1659
|
+
if (error) {
|
|
1660
|
+
outputError2("Failed to list evaluation sets", error);
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
set.command("remove").description("Remove an evaluation set").argument("<id>", "Evaluation set ID, name, or file base name").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (id, options) => {
|
|
1664
|
+
const [error] = await catchError((async () => {
|
|
1665
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1666
|
+
const service = new FlowEvalService;
|
|
1667
|
+
const removed = await service.removeEvalSet(options.path, id);
|
|
1668
|
+
if (!removed) {
|
|
1669
|
+
throw new Error(`Evaluation set "${id}" not found`);
|
|
1670
|
+
}
|
|
1671
|
+
OutputFormatter.success({
|
|
1672
|
+
Result: RESULTS.Success,
|
|
1673
|
+
Code: "FlowEvalSetRemove",
|
|
1674
|
+
Data: { Status: "Evaluation set removed", Id: id }
|
|
1675
|
+
});
|
|
1676
|
+
})());
|
|
1677
|
+
if (error) {
|
|
1678
|
+
outputError2("Failed to remove evaluation set", error);
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
function registerEvaluationCommands(evalCmd) {
|
|
1683
|
+
evalCmd.command("add").description("Add a data point to an evaluation set").argument("<name>", "Data point name").requiredOption("--set <name>", "Evaluation set name or ID").option("--inputs <json>", "Input values as a JSON object").option("--input-file <key=path>", "Attach a file as input <key>; repeatable", collectInputFile, []).option("--expected <json>", "Expected output as a JSON object").option("--criteria <json>", "Per-evaluator criteria JSON object keyed by evaluator id").option("--search-text <text>", "Search text for contains evaluators").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (name, options) => {
|
|
1684
|
+
const [error] = await catchError((async () => {
|
|
1685
|
+
if (options.inputs === undefined && options.inputFile.length === 0) {
|
|
1686
|
+
throw new Error("Provide --inputs and/or --input-file");
|
|
1687
|
+
}
|
|
1688
|
+
const inputs = options.inputs === undefined ? {} : parseJsonObject(options.inputs, "--inputs");
|
|
1689
|
+
const inputFiles = parseInputFiles(options.inputFile);
|
|
1690
|
+
const expectedOutput = options.expected ? parseJsonObject(options.expected, "--expected") : undefined;
|
|
1691
|
+
const criteria = options.criteria ? parseCriteriaOption(options.criteria) : undefined;
|
|
1692
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1693
|
+
const service = new FlowEvalService;
|
|
1694
|
+
const evaluation = await service.addEvaluation(options.path, options.set, {
|
|
1695
|
+
name,
|
|
1696
|
+
inputs,
|
|
1697
|
+
inputFiles,
|
|
1698
|
+
expectedOutput,
|
|
1699
|
+
criteria,
|
|
1700
|
+
searchText: options.searchText
|
|
1701
|
+
});
|
|
1702
|
+
OutputFormatter.success({
|
|
1703
|
+
Result: RESULTS.Success,
|
|
1704
|
+
Code: "FlowEvalAdd",
|
|
1705
|
+
Data: {
|
|
1706
|
+
Status: "Data point added",
|
|
1707
|
+
Name: evaluation.name,
|
|
1708
|
+
Id: evaluation.id,
|
|
1709
|
+
Set: options.set
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
})());
|
|
1713
|
+
if (error) {
|
|
1714
|
+
outputError2("Failed to add evaluation data point", error);
|
|
1715
|
+
}
|
|
1716
|
+
});
|
|
1717
|
+
evalCmd.command("list").description("List data points in an evaluation set").requiredOption("--set <name>", "Evaluation set name or ID").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (options) => {
|
|
1718
|
+
const [error] = await catchError((async () => {
|
|
1719
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1720
|
+
const service = new FlowEvalService;
|
|
1721
|
+
const evaluations = await service.listEvaluations(options.path, options.set);
|
|
1722
|
+
OutputFormatter.success({
|
|
1723
|
+
Result: RESULTS.Success,
|
|
1724
|
+
Code: "FlowEvalList",
|
|
1725
|
+
Data: evaluations.map((item) => ({
|
|
1726
|
+
Name: item.name,
|
|
1727
|
+
Id: item.id,
|
|
1728
|
+
Inputs: JSON.stringify(item.inputs),
|
|
1729
|
+
Expected: item.expectedOutput ? JSON.stringify(item.expectedOutput) : "-",
|
|
1730
|
+
Evaluators: Object.keys(item.evaluationCriterias ?? {}).length
|
|
1731
|
+
}))
|
|
1732
|
+
});
|
|
1733
|
+
})());
|
|
1734
|
+
if (error) {
|
|
1735
|
+
outputError2("Failed to list evaluation data points", error);
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
evalCmd.command("remove").description("Remove a data point from an evaluation set").argument("<id>", "Data point ID or name").requiredOption("--set <name>", "Evaluation set name or ID").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (id, options) => {
|
|
1739
|
+
const [error] = await catchError((async () => {
|
|
1740
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1741
|
+
const service = new FlowEvalService;
|
|
1742
|
+
const removed = await service.removeEvaluation(options.path, options.set, id);
|
|
1743
|
+
if (!removed) {
|
|
1744
|
+
throw new Error(`Data point "${id}" not found`);
|
|
1745
|
+
}
|
|
1746
|
+
OutputFormatter.success({
|
|
1747
|
+
Result: RESULTS.Success,
|
|
1748
|
+
Code: "FlowEvalRemove",
|
|
1749
|
+
Data: { Status: "Data point removed", Id: id }
|
|
1750
|
+
});
|
|
1751
|
+
})());
|
|
1752
|
+
if (error) {
|
|
1753
|
+
outputError2("Failed to remove evaluation data point", error);
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
function registerSimulationCommands(evalCmd) {
|
|
1758
|
+
const simulation = evalCmd.command("simulation").description("Manage simulations on eval data points");
|
|
1759
|
+
simulation.command("add").description("Add or update a simulation on a data point").argument("<component-id>", "Component ID to simulate").requiredOption("--set <name>", "Evaluation set name or ID").requiredOption("--data-point <id>", "Data point name or ID").requiredOption("--strategy <strategy>", "Simulation strategy: Static or Llm").option("--component-type <type>", "Component type (defaults to Node when --parent is used)").option("--component-description <text>", "Component description").option("--simulation-instructions <text>", "Simulation instructions (for Llm strategy)").option("--mock-value <json>", "Mock output value as JSON (for Static strategy)").option("--parent <component-id>", "Parent agent component ID. When set, the simulation is added as a child tool simulation on the parent agent node.").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (componentId, options) => {
|
|
1760
|
+
const [error] = await catchError((async () => {
|
|
1761
|
+
const strategy = options.strategy;
|
|
1762
|
+
if (strategy !== "Static" && strategy !== "Llm") {
|
|
1763
|
+
throw new Error(`--strategy must be Static or Llm (got "${strategy}")`);
|
|
1764
|
+
}
|
|
1765
|
+
const componentType = options.componentType ?? (options.parent !== undefined ? "Node" : undefined);
|
|
1766
|
+
if (componentType === undefined) {
|
|
1767
|
+
throw new Error("--component-type is required (or use --parent to default to Node)");
|
|
1768
|
+
}
|
|
1769
|
+
if (strategy === "Static" && options.mockValue === undefined) {
|
|
1770
|
+
throw new Error("--mock-value is required for the Static strategy");
|
|
1771
|
+
}
|
|
1772
|
+
if (strategy === "Llm" && options.simulationInstructions === undefined) {
|
|
1773
|
+
throw new Error("--simulation-instructions is required for the Llm strategy");
|
|
1774
|
+
}
|
|
1775
|
+
const mockValue = options.mockValue !== undefined ? parseJsonValue(options.mockValue, "--mock-value") : undefined;
|
|
1776
|
+
const simulation2 = {
|
|
1777
|
+
componentId,
|
|
1778
|
+
componentType,
|
|
1779
|
+
componentDescription: options.componentDescription,
|
|
1780
|
+
simulationStrategy: strategy,
|
|
1781
|
+
simulationInstruction: options.simulationInstructions,
|
|
1782
|
+
mockValue
|
|
1783
|
+
};
|
|
1784
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1785
|
+
const service = new FlowEvalService;
|
|
1786
|
+
if (options.parent !== undefined) {
|
|
1787
|
+
const created = await service.addChildSimulation(options.path, options.set, options.dataPoint, options.parent, simulation2);
|
|
1788
|
+
OutputFormatter.success({
|
|
1789
|
+
Result: RESULTS.Success,
|
|
1790
|
+
Code: "FlowEvalChildSimulationAdd",
|
|
1791
|
+
Data: {
|
|
1792
|
+
ComponentId: created.componentId,
|
|
1793
|
+
ComponentType: created.componentType,
|
|
1794
|
+
Strategy: created.simulationStrategy,
|
|
1795
|
+
Parent: options.parent,
|
|
1796
|
+
DataPoint: options.dataPoint,
|
|
1797
|
+
Set: options.set
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
} else {
|
|
1801
|
+
const created = await service.addSimulation(options.path, options.set, options.dataPoint, simulation2);
|
|
1802
|
+
OutputFormatter.success({
|
|
1803
|
+
Result: RESULTS.Success,
|
|
1804
|
+
Code: "FlowEvalSimulationAdd",
|
|
1805
|
+
Data: {
|
|
1806
|
+
ComponentId: created.componentId,
|
|
1807
|
+
ComponentType: created.componentType,
|
|
1808
|
+
Strategy: created.simulationStrategy,
|
|
1809
|
+
DataPoint: options.dataPoint,
|
|
1810
|
+
Set: options.set
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
})());
|
|
1815
|
+
if (error) {
|
|
1816
|
+
outputError2("Failed to add simulation", error);
|
|
1817
|
+
}
|
|
1818
|
+
});
|
|
1819
|
+
simulation.command("list").description("List simulations on a data point").requiredOption("--set <name>", "Evaluation set name or ID").requiredOption("--data-point <id>", "Data point name or ID").option("--parent <component-id>", "Parent agent component ID. When set, lists child tool simulations on the parent agent node.").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (options) => {
|
|
1820
|
+
const [error] = await catchError((async () => {
|
|
1821
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1822
|
+
const service = new FlowEvalService;
|
|
1823
|
+
const simulations = options.parent !== undefined ? await service.listChildSimulations(options.path, options.set, options.dataPoint, options.parent) : await service.listSimulations(options.path, options.set, options.dataPoint);
|
|
1824
|
+
OutputFormatter.success({
|
|
1825
|
+
Result: RESULTS.Success,
|
|
1826
|
+
Code: options.parent !== undefined ? "FlowEvalChildSimulationList" : "FlowEvalSimulationList",
|
|
1827
|
+
Data: simulations.map((s) => ({
|
|
1828
|
+
ComponentId: s.componentId,
|
|
1829
|
+
ComponentType: s.componentType,
|
|
1830
|
+
ComponentDescription: s.componentDescription ?? "-",
|
|
1831
|
+
Strategy: s.simulationStrategy,
|
|
1832
|
+
Instruction: s.simulationInstruction ?? "-",
|
|
1833
|
+
...options.parent !== undefined ? { Parent: options.parent } : {}
|
|
1834
|
+
}))
|
|
1835
|
+
});
|
|
1836
|
+
})());
|
|
1837
|
+
if (error) {
|
|
1838
|
+
outputError2("Failed to list simulations", error);
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
simulation.command("remove").description("Remove a simulation from a data point").argument("<component-id>", "Component ID to remove").requiredOption("--set <name>", "Evaluation set name or ID").requiredOption("--data-point <id>", "Data point name or ID").option("--parent <component-id>", "Parent agent component ID. When set, removes a child tool simulation from the parent agent node.").option("--path <path>", FLOW_EVAL_PATH_OPTION_DESCRIPTION2, ".").trackedAction(processContext, async (componentId, options) => {
|
|
1842
|
+
const [error] = await catchError((async () => {
|
|
1843
|
+
const { FlowEvalService } = await import("./flow-eval-service-ft8e16jz.js");
|
|
1844
|
+
const service = new FlowEvalService;
|
|
1845
|
+
const removed = options.parent !== undefined ? await service.removeChildSimulation(options.path, options.set, options.dataPoint, options.parent, componentId) : await service.removeSimulation(options.path, options.set, options.dataPoint, componentId);
|
|
1846
|
+
if (!removed) {
|
|
1847
|
+
throw new Error(options.parent !== undefined ? `Child simulation for component "${componentId}" not found on parent "${options.parent}"` : `Simulation for component "${componentId}" not found`);
|
|
1848
|
+
}
|
|
1849
|
+
OutputFormatter.success({
|
|
1850
|
+
Result: RESULTS.Success,
|
|
1851
|
+
Code: options.parent !== undefined ? "FlowEvalChildSimulationRemove" : "FlowEvalSimulationRemove",
|
|
1852
|
+
Data: {
|
|
1853
|
+
Status: options.parent !== undefined ? "Child simulation removed" : "Simulation removed",
|
|
1854
|
+
ComponentId: componentId,
|
|
1855
|
+
...options.parent !== undefined ? { Parent: options.parent } : {}
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
})());
|
|
1859
|
+
if (error) {
|
|
1860
|
+
outputError2("Failed to remove simulation", error);
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// src/commands/hitl.ts
|
|
1866
|
+
var VALID_PRIORITIES = ["Low", "Medium", "High"];
|
|
1867
|
+
function parsePosition(value) {
|
|
1868
|
+
const parts = value.split(",");
|
|
1869
|
+
if (parts.length !== 2) {
|
|
1870
|
+
throw new Error(`Invalid position format: "${value}". Expected "x,y" (e.g. 250,150)`);
|
|
1871
|
+
}
|
|
1872
|
+
const x = Number(parts[0]);
|
|
1873
|
+
const y = Number(parts[1]);
|
|
1874
|
+
if (Number.isNaN(x) || Number.isNaN(y)) {
|
|
1875
|
+
throw new Error(`Invalid position values: "${value}". x and y must be numbers`);
|
|
1876
|
+
}
|
|
1877
|
+
return { x, y };
|
|
1878
|
+
}
|
|
1879
|
+
function parsePositionOption(raw) {
|
|
1880
|
+
return catchError(() => parsePosition(raw));
|
|
1881
|
+
}
|
|
1882
|
+
function parseSchemaOption(raw) {
|
|
1883
|
+
const [parseErr, parsed] = catchError(() => JSON.parse(raw));
|
|
1884
|
+
if (parseErr)
|
|
1885
|
+
return [parseErr, undefined];
|
|
1886
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1887
|
+
return [new Error("--schema must be a JSON object"), undefined];
|
|
1888
|
+
}
|
|
1889
|
+
const obj = parsed;
|
|
1890
|
+
for (const key of ["inputs", "outputs", "inOuts", "outcomes"]) {
|
|
1891
|
+
const val = obj[key];
|
|
1892
|
+
if (val === undefined)
|
|
1893
|
+
continue;
|
|
1894
|
+
if (!Array.isArray(val)) {
|
|
1895
|
+
return [new Error(`--schema "${key}" must be an array`), undefined];
|
|
1896
|
+
}
|
|
1897
|
+
const missing = val.findIndex((item) => item === null || typeof item !== "object" || typeof item.name !== "string");
|
|
1898
|
+
if (missing !== -1) {
|
|
1899
|
+
return [
|
|
1900
|
+
new Error(`--schema "${key}[${missing}]" must have a "name" string field`),
|
|
1901
|
+
undefined
|
|
1902
|
+
];
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
return [undefined, parsed];
|
|
1906
|
+
}
|
|
1907
|
+
var FLOW_HITL_ADD_EXAMPLES = [
|
|
1908
|
+
{
|
|
1909
|
+
Description: "Add a bare HITL node with no schema fields",
|
|
1910
|
+
Command: "uip maestro flow hitl add workflow.flow",
|
|
1911
|
+
Output: {
|
|
1912
|
+
Code: "HitlNodeAdded",
|
|
1913
|
+
Data: {
|
|
1914
|
+
NodeId: "humanInTheLoop1",
|
|
1915
|
+
NodeType: "uipath.human-in-the-loop",
|
|
1916
|
+
Label: "Human in the Loop",
|
|
1917
|
+
DefinitionAdded: true
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
Description: "Add a HITL node with label, priority, and group assignee",
|
|
1923
|
+
Command: 'uip maestro flow hitl add workflow.flow --label "Invoice Review" --priority High --assignee finance-approvers',
|
|
1924
|
+
Output: {
|
|
1925
|
+
Code: "HitlNodeAdded",
|
|
1926
|
+
Data: {
|
|
1927
|
+
NodeId: "invoiceReview1",
|
|
1928
|
+
NodeType: "uipath.human-in-the-loop",
|
|
1929
|
+
Label: "Invoice Review",
|
|
1930
|
+
DefinitionAdded: true
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
{
|
|
1935
|
+
Description: "Add a HITL node with full schema (inputs, outputs, and outcomes)",
|
|
1936
|
+
Command: `uip maestro flow hitl add workflow.flow --label "Approval" --schema '{"inputs":[{"name":"invoiceId","binding":"fetchInvoice1.result.invoiceId"}],"outputs":[{"name":"decision","required":true}],"outcomes":[{"name":"Approve"},{"name":"Reject"}]}'`,
|
|
1937
|
+
Output: {
|
|
1938
|
+
Code: "HitlNodeAdded",
|
|
1939
|
+
Data: {
|
|
1940
|
+
NodeId: "approval1",
|
|
1941
|
+
NodeType: "uipath.human-in-the-loop",
|
|
1942
|
+
Label: "Approval",
|
|
1943
|
+
DefinitionAdded: true
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
];
|
|
1948
|
+
var registerHitlCommand = (program) => {
|
|
1949
|
+
const hitl = program.command("hitl").description("Add HITL (Human-in-the-Loop) nodes to a Flow file");
|
|
1950
|
+
hitl.command("add").description("Add a Human-in-the-Loop node to a .flow file. Pauses flow execution and waits for a human to complete a task in Action Center.").argument("<file>", "Path to the .flow file").option("--schema <json>", 'Task schema. inputs pre-fill from flow variables (add "binding":"varName"), outputs write back (add "variable":"varName"). ' + `e.g. '{"title":"Invoice Approval","inputs":[{"name":"invoiceId"}],"outputs":[{"name":"decision","variable":"approvalDecision","required":true}],"outcomes":[{"name":"Approve"},{"name":"Reject"}]}'`).option("--priority <level>", "Task priority: Low, Medium, High (default: Low)").option("--position <x,y>", "Canvas position for the node (e.g. 250,300)").option("--label <label>", "Display label for the node").option("--assignee <email-or-group>", "Assignment criteria: user email (e.g. reviewer@company.com) or group name (e.g. finance-approvers)").examples(FLOW_HITL_ADD_EXAMPLES).trackedAction(processContext, async (file, options) => {
|
|
1951
|
+
if (options.priority !== undefined && !VALID_PRIORITIES.includes(options.priority)) {
|
|
1952
|
+
OutputFormatter.error({
|
|
1953
|
+
Result: RESULTS.Failure,
|
|
1954
|
+
Message: "Invalid priority",
|
|
1955
|
+
Instructions: `Priority must be one of: ${VALID_PRIORITIES.join(", ")}`
|
|
1956
|
+
});
|
|
1957
|
+
processContext.exit(1);
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
let position;
|
|
1961
|
+
if (options.position !== undefined) {
|
|
1962
|
+
const [posError, parsed] = parsePositionOption(options.position);
|
|
1963
|
+
if (posError) {
|
|
1964
|
+
OutputFormatter.error({
|
|
1965
|
+
Result: RESULTS.Failure,
|
|
1966
|
+
Message: "Invalid --position value",
|
|
1967
|
+
Instructions: extractErrorMessageSync(posError)
|
|
1968
|
+
});
|
|
1969
|
+
processContext.exit(1);
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
position = parsed;
|
|
1973
|
+
}
|
|
1974
|
+
let schema;
|
|
1975
|
+
if (options.schema !== undefined) {
|
|
1976
|
+
const [schemaError, parsed] = parseSchemaOption(options.schema);
|
|
1977
|
+
if (schemaError) {
|
|
1978
|
+
OutputFormatter.error({
|
|
1979
|
+
Result: RESULTS.Failure,
|
|
1980
|
+
Message: "Invalid --schema JSON",
|
|
1981
|
+
Instructions: extractErrorMessageSync(schemaError)
|
|
1982
|
+
});
|
|
1983
|
+
processContext.exit(1);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
schema = parsed;
|
|
1987
|
+
}
|
|
1988
|
+
const { addHitlNode } = await import("./hitl-service-kvt5wms4.js");
|
|
1989
|
+
const [error, result] = await catchError(addHitlNode(file, {
|
|
1990
|
+
schema,
|
|
1991
|
+
priority: options.priority,
|
|
1992
|
+
position,
|
|
1993
|
+
label: options.label,
|
|
1994
|
+
assignee: options.assignee
|
|
1995
|
+
}));
|
|
1996
|
+
if (error) {
|
|
1997
|
+
OutputFormatter.error({
|
|
1998
|
+
Result: RESULTS.Failure,
|
|
1999
|
+
Message: "Failed to add HITL node",
|
|
2000
|
+
Instructions: extractErrorMessageSync(error)
|
|
2001
|
+
});
|
|
2002
|
+
processContext.exit(1);
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
OutputFormatter.success({
|
|
2006
|
+
Result: RESULTS.Success,
|
|
2007
|
+
Code: "HitlNodeAdded",
|
|
2008
|
+
Data: {
|
|
2009
|
+
NodeId: result.nodeId,
|
|
2010
|
+
NodeType: result.nodeType,
|
|
2011
|
+
Label: result.label,
|
|
2012
|
+
DefinitionAdded: result.definitionAdded
|
|
2013
|
+
}
|
|
2014
|
+
});
|
|
2015
|
+
});
|
|
2016
|
+
};
|
|
2017
|
+
|
|
2018
|
+
// src/commands/incidents.ts
|
|
2019
|
+
var registerIncidentsCommand = (program) => {
|
|
2020
|
+
registerSharedIncidentCommands(program, {
|
|
2021
|
+
toolLabel: "Flow"
|
|
2022
|
+
});
|
|
2023
|
+
};
|
|
2024
|
+
|
|
2025
|
+
// src/commands/init.ts
|
|
2026
|
+
var FLOW_INIT_EXAMPLES = [
|
|
2027
|
+
{
|
|
2028
|
+
Description: "Create a new flow project. If the current directory is not already inside a solution, a parent solution named '<project>Solution' is created automatically and the flow project is nested inside it.",
|
|
2029
|
+
Command: "uip maestro flow init my-flow-project",
|
|
2030
|
+
Output: {
|
|
2031
|
+
Code: "FlowInit",
|
|
2032
|
+
Data: {
|
|
2033
|
+
Status: "Created successfully",
|
|
2034
|
+
Path: "/workspace/my-flow-projectSolution/my-flow-project",
|
|
2035
|
+
SolutionRegistration: {
|
|
2036
|
+
Status: "Registered",
|
|
2037
|
+
Message: "Project registered in parent solution.",
|
|
2038
|
+
Solution: "/workspace/my-flow-projectSolution/my-flow-projectSolution.uipx",
|
|
2039
|
+
Project: "my-flow-project/project.uiproj",
|
|
2040
|
+
ProjectId: "00000000-0000-0000-0000-000000000000",
|
|
2041
|
+
Instructions: `The project is listed in the parent solution manifest.
|
|
2042
|
+
Packing or uploading the solution will sync solution resources automatically.`
|
|
2043
|
+
},
|
|
2044
|
+
AutoCreatedSolution: {
|
|
2045
|
+
Name: "my-flow-projectSolution",
|
|
2046
|
+
Path: "/workspace/my-flow-projectSolution",
|
|
2047
|
+
SolutionFile: "/workspace/my-flow-projectSolution/my-flow-projectSolution.uipx"
|
|
2048
|
+
},
|
|
2049
|
+
ProjectArtifacts: { Created: true },
|
|
2050
|
+
NextSteps: `The project is listed in the parent solution manifest.
|
|
2051
|
+
Packing or uploading the solution will sync solution resources automatically.`
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
];
|
|
2056
|
+
var registerInitCommand = (program) => {
|
|
2057
|
+
program.command("init").description("Create a new Flow project with boilerplate files. If run outside an existing solution, a parent '<name>Solution' is scaffolded automatically and the flow project is created inside it. If run inside an existing solution, the project is added to that solution.").argument("<name>", "Flow project name").option("--force", "Force initialization even if target directory exists and is not empty").option("--skip-solution-registration", "Do not auto-register this project in the surrounding solution").examples(FLOW_INIT_EXAMPLES).trackedAction(processContext, async (name, options) => {
|
|
2058
|
+
const { flowInitAsync } = await import("./flow-init-service-mva4crty.js");
|
|
2059
|
+
const [error, result] = await catchError(flowInitAsync(name, {
|
|
2060
|
+
force: options.force,
|
|
2061
|
+
skipRegistration: options.skipSolutionRegistration === true
|
|
2062
|
+
}));
|
|
2063
|
+
if (error) {
|
|
2064
|
+
OutputFormatter.error({
|
|
2065
|
+
Result: RESULTS.Failure,
|
|
2066
|
+
Message: "Failed to create flow project",
|
|
2067
|
+
Instructions: error.message
|
|
2068
|
+
});
|
|
2069
|
+
processContext.exit(1);
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
const data = {
|
|
2073
|
+
Status: "Created successfully",
|
|
2074
|
+
Path: result.projectDir,
|
|
2075
|
+
SolutionRegistration: result.registration
|
|
2076
|
+
};
|
|
2077
|
+
if (result.autoCreatedSolution) {
|
|
2078
|
+
data.AutoCreatedSolution = result.autoCreatedSolution;
|
|
2079
|
+
}
|
|
2080
|
+
if (result.projectArtifacts) {
|
|
2081
|
+
data.ProjectArtifacts = result.projectArtifacts;
|
|
2082
|
+
}
|
|
2083
|
+
if (result.registration.Instructions) {
|
|
2084
|
+
data.NextSteps = result.registration.Instructions;
|
|
2085
|
+
}
|
|
2086
|
+
OutputFormatter.success({
|
|
2087
|
+
Result: RESULTS.Success,
|
|
2088
|
+
Code: "FlowInit",
|
|
2089
|
+
Data: data
|
|
2090
|
+
});
|
|
2091
|
+
});
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
// src/commands/instances.ts
|
|
2095
|
+
var registerInstancesCommand = (program) => {
|
|
2096
|
+
registerSharedInstanceCommands(program, {
|
|
2097
|
+
toolLabel: "Flow",
|
|
2098
|
+
assetType: "flow",
|
|
2099
|
+
processType: "Flow"
|
|
2100
|
+
});
|
|
2101
|
+
};
|
|
2102
|
+
|
|
2103
|
+
// src/commands/job.ts
|
|
2104
|
+
var VALID_JOB_STATES = [
|
|
2105
|
+
"Pending",
|
|
2106
|
+
"Running",
|
|
2107
|
+
"Successful",
|
|
2108
|
+
"Faulted",
|
|
2109
|
+
"Stopped",
|
|
2110
|
+
"Stopping",
|
|
2111
|
+
"Suspended",
|
|
2112
|
+
"Resumed",
|
|
2113
|
+
"Terminating"
|
|
2114
|
+
];
|
|
2115
|
+
var DEFAULT_JOB_LIST_LIMIT = "50";
|
|
2116
|
+
var DEFAULT_JOB_LIST_OFFSET = "0";
|
|
2117
|
+
var DEFAULT_JOB_LIST_ORDER_BY = "Id desc";
|
|
2118
|
+
var FLOW_JOB_LIST_EXAMPLES = [
|
|
2119
|
+
{
|
|
2120
|
+
Description: "List recent Flow jobs in a folder",
|
|
2121
|
+
Command: "uip maestro flow job list --folder-key c3d4e5f6-0000-0000-0000-000000000001 --limit 2",
|
|
2122
|
+
Output: {
|
|
2123
|
+
Code: "FlowJobList",
|
|
2124
|
+
Data: [
|
|
2125
|
+
{
|
|
2126
|
+
key: "b2c3d4e5-0000-0000-0000-000000000001",
|
|
2127
|
+
state: "Successful",
|
|
2128
|
+
processName: "InvoiceFlow",
|
|
2129
|
+
source: "Manual",
|
|
2130
|
+
startTime: "2025-04-15T10:30:00Z",
|
|
2131
|
+
endTime: "2025-04-15T10:32:11Z"
|
|
2132
|
+
}
|
|
2133
|
+
]
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
];
|
|
2137
|
+
var FLOW_JOB_STATUS_EXAMPLES = [
|
|
2138
|
+
{
|
|
2139
|
+
Description: "Get detailed status of a flow job",
|
|
2140
|
+
Command: "uip maestro flow job status b2c3d4e5-0000-0000-0000-000000000001",
|
|
2141
|
+
Output: {
|
|
2142
|
+
Code: "FlowJobStatus",
|
|
2143
|
+
Data: {
|
|
2144
|
+
key: "b2c3d4e5-0000-0000-0000-000000000001",
|
|
2145
|
+
state: "Successful",
|
|
2146
|
+
startTime: "2025-04-15T10:30:00Z",
|
|
2147
|
+
endTime: "2025-04-15T10:32:11Z",
|
|
2148
|
+
releaseName: "InvoiceFlow",
|
|
2149
|
+
processKey: "a1b2c3d4-0000-0000-0000-000000000001"
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
];
|
|
2154
|
+
var FLOW_JOB_TRACES_ERROR_INSTRUCTIONS = "Check that the job started successfully and that traces are available before calling this command.";
|
|
2155
|
+
function parsePositiveInteger(value, flag) {
|
|
2156
|
+
const candidate = value ?? "";
|
|
2157
|
+
if (!/^\d+$/.test(candidate)) {
|
|
2158
|
+
throw new Error(`Invalid ${flag} value`);
|
|
2159
|
+
}
|
|
2160
|
+
const parsed = Number.parseInt(candidate, 10);
|
|
2161
|
+
if (parsed <= 0) {
|
|
2162
|
+
throw new Error(`Invalid ${flag} value`);
|
|
2163
|
+
}
|
|
2164
|
+
return parsed;
|
|
2165
|
+
}
|
|
2166
|
+
function parseNonNegativeInteger(value, flag) {
|
|
2167
|
+
const candidate = value ?? "";
|
|
2168
|
+
if (!/^\d+$/.test(candidate)) {
|
|
2169
|
+
throw new Error(`Invalid ${flag} value`);
|
|
2170
|
+
}
|
|
2171
|
+
return Number.parseInt(candidate, 10);
|
|
2172
|
+
}
|
|
2173
|
+
function normalizeOptionalOption(value) {
|
|
2174
|
+
return value === undefined ? undefined : value.trim();
|
|
2175
|
+
}
|
|
2176
|
+
function formatDate(value) {
|
|
2177
|
+
if (!value) {
|
|
2178
|
+
return "";
|
|
2179
|
+
}
|
|
2180
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
2181
|
+
return Number.isNaN(date.getTime()) ? String(value) : date.toISOString();
|
|
2182
|
+
}
|
|
2183
|
+
function formatFlowJob(job) {
|
|
2184
|
+
return {
|
|
2185
|
+
key: job.key ?? "",
|
|
2186
|
+
state: job.state ?? "",
|
|
2187
|
+
processName: job.releaseName ?? "",
|
|
2188
|
+
releaseKey: job.release?.key ?? "",
|
|
2189
|
+
processKey: job.release?.processKey ?? "",
|
|
2190
|
+
source: job.sourceType ?? "",
|
|
2191
|
+
type: job.type ?? "",
|
|
2192
|
+
priority: job.jobPriority ?? "",
|
|
2193
|
+
machine: job.hostMachineName ?? "",
|
|
2194
|
+
machineKey: job.machine?.key ?? "",
|
|
2195
|
+
folderKey: job.folderKey ?? "",
|
|
2196
|
+
startTime: formatDate(job.startTime),
|
|
2197
|
+
endTime: formatDate(job.endTime),
|
|
2198
|
+
creationTime: formatDate(job.creationTime)
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
function registerJobCommand(program) {
|
|
2202
|
+
const jobCmd = program.command("job").description("Manage Flow jobs");
|
|
2203
|
+
jobCmd.command("list").description("List Flow jobs in a folder").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-key <key>", "Folder key (GUID, defaults to authenticated folder)").option("--state <state>", `Filter by job state (${VALID_JOB_STATES.join(", ")})`).option("--process-name <name>", "Filter by Flow process name (contains match)").option("-l, --limit <number>", "Number of items to return", DEFAULT_JOB_LIST_LIMIT).option("--offset <number>", "Number of items to skip", DEFAULT_JOB_LIST_OFFSET).option("--sort-by <field>", "Sort results by field (e.g., 'Id desc')", DEFAULT_JOB_LIST_ORDER_BY).option("--all-fields", "Return all fields from the API response instead of a curated summary").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", (value) => Number.parseInt(value, 10), 10).examples(FLOW_JOB_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
2204
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
2205
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
2206
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
2207
|
+
}));
|
|
2208
|
+
if (loginError) {
|
|
2209
|
+
OutputFormatter.error({
|
|
2210
|
+
Result: RESULTS.Failure,
|
|
2211
|
+
Message: loginError.message,
|
|
2212
|
+
Instructions: instructionsFor("auth", loginError)
|
|
2213
|
+
});
|
|
2214
|
+
processContext.exit(1);
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
2218
|
+
OutputFormatter.error({
|
|
2219
|
+
Result: RESULTS.Failure,
|
|
2220
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
2221
|
+
Instructions: instructionsFor("auth")
|
|
2222
|
+
});
|
|
2223
|
+
processContext.exit(1);
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
const normalizedOptions = {
|
|
2227
|
+
tenant: normalizeOptionalOption(options.tenant),
|
|
2228
|
+
folderKey: normalizeOptionalOption(options.folderKey),
|
|
2229
|
+
state: normalizeOptionalOption(options.state),
|
|
2230
|
+
processName: normalizeOptionalOption(options.processName),
|
|
2231
|
+
orderBy: normalizeOptionalOption(options.sortBy)
|
|
2232
|
+
};
|
|
2233
|
+
const tenantName = normalizedOptions.tenant || loginStatus.tenantName;
|
|
2234
|
+
if (!tenantName || !loginStatus.organizationId) {
|
|
2235
|
+
OutputFormatter.error({
|
|
2236
|
+
Result: RESULTS.Failure,
|
|
2237
|
+
Message: "Missing tenant or organization info.",
|
|
2238
|
+
Instructions: instructionsFor("identity")
|
|
2239
|
+
});
|
|
2240
|
+
processContext.exit(1);
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
const state = normalizedOptions.state;
|
|
2244
|
+
if (state && !VALID_JOB_STATES.includes(state)) {
|
|
2245
|
+
OutputFormatter.error({
|
|
2246
|
+
Result: RESULTS.Failure,
|
|
2247
|
+
Message: "Invalid --state value.",
|
|
2248
|
+
Instructions: `Use one of: ${VALID_JOB_STATES.join(", ")}.`
|
|
2249
|
+
});
|
|
2250
|
+
processContext.exit(1);
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
let limit;
|
|
2254
|
+
let offset;
|
|
2255
|
+
try {
|
|
2256
|
+
limit = parsePositiveInteger(options.limit ?? DEFAULT_JOB_LIST_LIMIT, "--limit");
|
|
2257
|
+
offset = parseNonNegativeInteger(options.offset ?? DEFAULT_JOB_LIST_OFFSET, "--offset");
|
|
2258
|
+
} catch (error) {
|
|
2259
|
+
OutputFormatter.error({
|
|
2260
|
+
Result: RESULTS.Failure,
|
|
2261
|
+
Message: error instanceof Error ? error.message : "Invalid pagination value.",
|
|
2262
|
+
Instructions: "Use a positive integer for --limit and a non-negative integer for --offset."
|
|
2263
|
+
});
|
|
2264
|
+
processContext.exit(1);
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
const config = {
|
|
2268
|
+
processKey: "",
|
|
2269
|
+
folderKey: normalizedOptions.folderKey ?? "",
|
|
2270
|
+
inputs: {},
|
|
2271
|
+
authToken: loginStatus.accessToken ?? "",
|
|
2272
|
+
organizationId: loginStatus.organizationId,
|
|
2273
|
+
tenantName,
|
|
2274
|
+
tenantId: loginStatus.tenantId,
|
|
2275
|
+
baseUrl: loginStatus.baseUrl
|
|
2276
|
+
};
|
|
2277
|
+
const { listFlowJobs } = await import("./flow-api-wv8p8xbd.js");
|
|
2278
|
+
const [listError, jobs] = await catchError(listFlowJobs(config, {
|
|
2279
|
+
state,
|
|
2280
|
+
processName: normalizedOptions.processName,
|
|
2281
|
+
limit,
|
|
2282
|
+
offset,
|
|
2283
|
+
orderBy: normalizedOptions.orderBy || DEFAULT_JOB_LIST_ORDER_BY
|
|
2284
|
+
}));
|
|
2285
|
+
if (listError) {
|
|
2286
|
+
OutputFormatter.error({
|
|
2287
|
+
Result: RESULTS.Failure,
|
|
2288
|
+
Message: listError.message,
|
|
2289
|
+
Instructions: instructionsFor("backend", listError)
|
|
2290
|
+
});
|
|
2291
|
+
processContext.exit(1);
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
const items = jobs ?? [];
|
|
2295
|
+
OutputFormatter.success({
|
|
2296
|
+
Result: RESULTS.Success,
|
|
2297
|
+
Code: "FlowJobList",
|
|
2298
|
+
Data: options.allFields ? items : items.map(formatFlowJob),
|
|
2299
|
+
Pagination: {
|
|
2300
|
+
Returned: items.length,
|
|
2301
|
+
Limit: limit,
|
|
2302
|
+
Offset: offset,
|
|
2303
|
+
HasMore: items.length >= limit
|
|
2304
|
+
}
|
|
2305
|
+
});
|
|
2306
|
+
});
|
|
2307
|
+
jobCmd.command("traces").description("Stream traces for a running job").argument("<job-key>", "Job key (GUID from run command)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--poll-interval <ms>", "Polling interval in milliseconds", "2000").option("--traces-service <name>", "Traces service name (default: llmopstenant_)", "llmopstenant_").option("--pretty", "Show traces in human-readable format instead of raw JSON").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).trackedAction(processContext, async (jobKey, options) => {
|
|
2308
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
2309
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
2310
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
2311
|
+
}));
|
|
2312
|
+
if (loginError) {
|
|
2313
|
+
OutputFormatter.error({
|
|
2314
|
+
Result: RESULTS.Failure,
|
|
2315
|
+
Message: loginError.message,
|
|
2316
|
+
Instructions: instructionsFor("auth", loginError)
|
|
2317
|
+
});
|
|
2318
|
+
processContext.exit(1);
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
2322
|
+
OutputFormatter.error({
|
|
2323
|
+
Result: RESULTS.Failure,
|
|
2324
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
2325
|
+
Instructions: instructionsFor("auth")
|
|
2326
|
+
});
|
|
2327
|
+
processContext.exit(1);
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
const tenantName = options.tenant || loginStatus.tenantName;
|
|
2331
|
+
if (!tenantName || !loginStatus.organizationId) {
|
|
2332
|
+
OutputFormatter.error({
|
|
2333
|
+
Result: RESULTS.Failure,
|
|
2334
|
+
Message: "Missing tenant or organization info.",
|
|
2335
|
+
Instructions: instructionsFor("identity")
|
|
2336
|
+
});
|
|
2337
|
+
processContext.exit(1);
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
const config = {
|
|
2341
|
+
processKey: "",
|
|
2342
|
+
folderKey: "",
|
|
2343
|
+
inputs: {},
|
|
2344
|
+
authToken: loginStatus.accessToken ?? "",
|
|
2345
|
+
organizationId: loginStatus.organizationId,
|
|
2346
|
+
tenantName,
|
|
2347
|
+
tenantId: loginStatus.tenantId,
|
|
2348
|
+
baseUrl: loginStatus.baseUrl,
|
|
2349
|
+
tracesServiceName: options.tracesService
|
|
2350
|
+
};
|
|
2351
|
+
const pollIntervalMs = parseInt(options.pollInterval || "2000", 10);
|
|
2352
|
+
if (!Number.isFinite(pollIntervalMs) || pollIntervalMs <= 0) {
|
|
2353
|
+
OutputFormatter.error({
|
|
2354
|
+
Result: RESULTS.Failure,
|
|
2355
|
+
Message: `Invalid poll-interval: ${options.pollInterval}. Must be a positive number.`,
|
|
2356
|
+
Instructions: "Provide a positive number in milliseconds."
|
|
2357
|
+
});
|
|
2358
|
+
processContext.exit(1);
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
const pollSeconds = pollIntervalMs / 1000;
|
|
2362
|
+
logger.info(`Streaming traces for job: ${jobKey}
|
|
2363
|
+
Polling every ${pollSeconds}s...`);
|
|
2364
|
+
const { formatTraceLine } = await import("./shared-utils-8xdq6f04.js");
|
|
2365
|
+
const { streamJobTraces } = await import("./flow-api-wv8p8xbd.js");
|
|
2366
|
+
const [tracesError, result] = await catchError(streamJobTraces(config, jobKey, {
|
|
2367
|
+
onTrace: (trace) => {
|
|
2368
|
+
if (options.pretty) {
|
|
2369
|
+
ScreenLogger.progress(formatTraceLine(trace));
|
|
2370
|
+
} else {
|
|
2371
|
+
ScreenLogger.progress(JSON.stringify(trace));
|
|
2372
|
+
}
|
|
2373
|
+
},
|
|
2374
|
+
onComplete: (result2) => {
|
|
2375
|
+
ScreenLogger.progress(`Job ${jobKey}: ${result2.finalState} (${result2.duration}ms)`);
|
|
2376
|
+
},
|
|
2377
|
+
onError: (error) => {
|
|
2378
|
+
ScreenLogger.progress(`Error: ${error.message}`);
|
|
2379
|
+
},
|
|
2380
|
+
pollInterval: pollIntervalMs
|
|
2381
|
+
}));
|
|
2382
|
+
if (tracesError) {
|
|
2383
|
+
OutputFormatter.error({
|
|
2384
|
+
Result: RESULTS.Failure,
|
|
2385
|
+
Message: tracesError.message,
|
|
2386
|
+
Instructions: FLOW_JOB_TRACES_ERROR_INSTRUCTIONS
|
|
2387
|
+
});
|
|
2388
|
+
processContext.exit(1);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
if (!result.success && result.error) {
|
|
2392
|
+
OutputFormatter.error({
|
|
2393
|
+
Result: RESULTS.Failure,
|
|
2394
|
+
Message: result.error,
|
|
2395
|
+
Instructions: FLOW_JOB_TRACES_ERROR_INSTRUCTIONS
|
|
2396
|
+
});
|
|
2397
|
+
processContext.exit(1);
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
processContext.exit(result.success ? 0 : 1);
|
|
2401
|
+
});
|
|
2402
|
+
jobCmd.command("status").description("Get detailed status of a Flow job").argument("<job-key>", "Job key (GUID from run command)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-key <key>", "Folder key (GUID from list command, defaults to authenticated folder)").option("--detailed", "Show full response with all fields").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(FLOW_JOB_STATUS_EXAMPLES).trackedAction(processContext, async (jobKey, options) => {
|
|
2403
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
2404
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
2405
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
2406
|
+
}));
|
|
2407
|
+
if (loginError) {
|
|
2408
|
+
OutputFormatter.error({
|
|
2409
|
+
Result: RESULTS.Failure,
|
|
2410
|
+
Message: loginError.message,
|
|
2411
|
+
Instructions: instructionsFor("auth", loginError)
|
|
2412
|
+
});
|
|
2413
|
+
processContext.exit(1);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
2417
|
+
OutputFormatter.error({
|
|
2418
|
+
Result: RESULTS.Failure,
|
|
2419
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
2420
|
+
Instructions: instructionsFor("auth")
|
|
2421
|
+
});
|
|
2422
|
+
processContext.exit(1);
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
const tenantName = options.tenant || loginStatus.tenantName;
|
|
2426
|
+
if (!tenantName || !loginStatus.organizationId) {
|
|
2427
|
+
OutputFormatter.error({
|
|
2428
|
+
Result: RESULTS.Failure,
|
|
2429
|
+
Message: "Missing tenant or organization info.",
|
|
2430
|
+
Instructions: instructionsFor("identity")
|
|
2431
|
+
});
|
|
2432
|
+
processContext.exit(1);
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
const config = {
|
|
2436
|
+
processKey: "",
|
|
2437
|
+
folderKey: options.folderKey ?? "",
|
|
2438
|
+
inputs: {},
|
|
2439
|
+
authToken: loginStatus.accessToken ?? "",
|
|
2440
|
+
organizationId: loginStatus.organizationId,
|
|
2441
|
+
tenantName,
|
|
2442
|
+
tenantId: loginStatus.tenantId,
|
|
2443
|
+
baseUrl: loginStatus.baseUrl
|
|
2444
|
+
};
|
|
2445
|
+
const { getJobStatusDetailed } = await import("./flow-api-wv8p8xbd.js");
|
|
2446
|
+
const [statusError, status] = await catchError(getJobStatusDetailed(config, jobKey));
|
|
2447
|
+
if (statusError) {
|
|
2448
|
+
OutputFormatter.error({
|
|
2449
|
+
Result: RESULTS.Failure,
|
|
2450
|
+
Message: statusError.message,
|
|
2451
|
+
Instructions: instructionsFor("backend", statusError)
|
|
2452
|
+
});
|
|
2453
|
+
processContext.exit(1);
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
const outputData = options.detailed ? status : {
|
|
2457
|
+
key: status.key,
|
|
2458
|
+
state: status.state,
|
|
2459
|
+
runtimeType: status.runtimeType,
|
|
2460
|
+
processType: status.processType,
|
|
2461
|
+
startTime: status.startTime,
|
|
2462
|
+
endTime: status.endTime,
|
|
2463
|
+
creationTime: status.creationTime,
|
|
2464
|
+
inputArguments: status.inputArguments,
|
|
2465
|
+
outputArguments: status.outputArguments,
|
|
2466
|
+
info: status.info,
|
|
2467
|
+
releaseName: status.release?.name,
|
|
2468
|
+
processKey: status.release?.processKey,
|
|
2469
|
+
robotName: status.robot?.name,
|
|
2470
|
+
hostMachineName: status.hostMachineName
|
|
2471
|
+
};
|
|
2472
|
+
const pendingSeconds = pendingSecondsFor(status.state, status.creationTime);
|
|
2473
|
+
OutputFormatter.success({
|
|
2474
|
+
Result: RESULTS.Success,
|
|
2475
|
+
Code: "FlowJobStatus",
|
|
2476
|
+
Data: pendingSeconds === undefined ? outputData : { ...outputData, pendingSeconds },
|
|
2477
|
+
...pendingSeconds === undefined ? {} : {
|
|
2478
|
+
Instructions: pendingStallInstructions(pendingSeconds)
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
// src/commands/migrate.ts
|
|
2485
|
+
var FLOW_MIGRATE_EXAMPLES = [
|
|
2486
|
+
{
|
|
2487
|
+
Description: "Migrate a .flow file to the current schema version",
|
|
2488
|
+
Command: "uip maestro flow migrate ./my-flow/my-flow.flow",
|
|
2489
|
+
Output: {
|
|
2490
|
+
Code: "FlowMigrate",
|
|
2491
|
+
Data: {
|
|
2492
|
+
Migrated: true,
|
|
2493
|
+
FromVersion: "1.0.0",
|
|
2494
|
+
ToVersion: "1.0",
|
|
2495
|
+
WorkflowSteps: ["1.0.0 -> 1.0"]
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
},
|
|
2499
|
+
{
|
|
2500
|
+
Description: "Preview changes without writing the file",
|
|
2501
|
+
Command: "uip maestro flow migrate ./my-flow/my-flow.flow --dry-run",
|
|
2502
|
+
Output: {
|
|
2503
|
+
Code: "FlowMigrate",
|
|
2504
|
+
Data: {
|
|
2505
|
+
Migrated: true,
|
|
2506
|
+
DryRun: true,
|
|
2507
|
+
FromVersion: "1.0.0",
|
|
2508
|
+
ToVersion: "1.0",
|
|
2509
|
+
WorkflowSteps: ["1.0.0 -> 1.0"]
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
{
|
|
2514
|
+
Description: "Skip optional node migrations (e.g. typeVersion normalization)",
|
|
2515
|
+
Command: "uip maestro flow migrate ./my-flow/my-flow.flow --no-include-optional",
|
|
2516
|
+
Output: {
|
|
2517
|
+
Code: "FlowMigrate",
|
|
2518
|
+
Data: {
|
|
2519
|
+
Migrated: true,
|
|
2520
|
+
FromVersion: "1.0.0",
|
|
2521
|
+
ToVersion: "1.0",
|
|
2522
|
+
NodeMigrations: { OptionalApplied: 0 }
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
];
|
|
2527
|
+
var registerMigrateCommand = (program) => {
|
|
2528
|
+
program.command("migrate").description("Migrate a .flow file to the current workflow and node schema versions").argument("<file>", "Path to the .flow file").option("--dry-run", "Show what would change without writing the file").option("--no-include-optional", "Skip optional node migrations (e.g. typeVersion normalization). Default: optional migrations are applied.").examples(FLOW_MIGRATE_EXAMPLES).trackedAction(processContext, async (file, options) => {
|
|
2529
|
+
const { migrateFlow } = await import("./flow-migrate-service-rwcdx60q.js");
|
|
2530
|
+
const [error, result] = await catchError(migrateFlow(file, {
|
|
2531
|
+
dryRun: options.dryRun,
|
|
2532
|
+
includeOptional: options.includeOptional
|
|
2533
|
+
}));
|
|
2534
|
+
if (error) {
|
|
2535
|
+
OutputFormatter.error({
|
|
2536
|
+
Result: RESULTS.Failure,
|
|
2537
|
+
Message: "Failed to migrate flow",
|
|
2538
|
+
Instructions: error.message
|
|
2539
|
+
});
|
|
2540
|
+
processContext.exit(1);
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
const instructions = result.migrated ? result.dryRun ? "Dry-run: file not modified. Re-run without --dry-run to apply." : `Migrated ${result.fromVersion} → ${result.toVersion}` : `Already at version ${result.toVersion}`;
|
|
2544
|
+
OutputFormatter.success({
|
|
2545
|
+
Result: RESULTS.Success,
|
|
2546
|
+
Code: "FlowMigrate",
|
|
2547
|
+
Data: {
|
|
2548
|
+
File: result.file,
|
|
2549
|
+
Migrated: result.migrated,
|
|
2550
|
+
DryRun: result.dryRun,
|
|
2551
|
+
FromVersion: result.fromVersion,
|
|
2552
|
+
ToVersion: result.toVersion,
|
|
2553
|
+
WorkflowSteps: result.workflowSteps,
|
|
2554
|
+
NodeMigrations: {
|
|
2555
|
+
ForcedAvailable: result.forcedNodeMigrations,
|
|
2556
|
+
OptionalAvailable: result.optionalNodeMigrations,
|
|
2557
|
+
OptionalApplied: result.optionalApplied,
|
|
2558
|
+
Failed: result.failedNodeMigrations
|
|
2559
|
+
}
|
|
2560
|
+
},
|
|
2561
|
+
Instructions: instructions
|
|
2562
|
+
});
|
|
2563
|
+
});
|
|
2564
|
+
};
|
|
2565
|
+
|
|
2566
|
+
// src/commands/node.ts
|
|
2567
|
+
var NODE_ADD_EXAMPLES = [
|
|
2568
|
+
{
|
|
2569
|
+
Description: "Add a manual trigger node to a flow",
|
|
2570
|
+
Command: 'uip maestro flow node add flow.json core.trigger.manual --label "Start"',
|
|
2571
|
+
Output: {
|
|
2572
|
+
Code: "NodeAddSuccess",
|
|
2573
|
+
Data: {
|
|
2574
|
+
Node: {
|
|
2575
|
+
id: "node_a1b2c3d4",
|
|
2576
|
+
type: "core.trigger.manual"
|
|
2577
|
+
},
|
|
2578
|
+
DefinitionAdded: true,
|
|
2579
|
+
BindingsCreated: 0,
|
|
2580
|
+
VariableCount: 0
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
];
|
|
2585
|
+
var NODE_CONFIGURE_EXAMPLES = [
|
|
2586
|
+
{
|
|
2587
|
+
Description: "Configure a connector node with connection details",
|
|
2588
|
+
Command: `uip maestro flow node configure flow.json node_a1b2c3d4 --detail '{"connectionId":"b2c3d4e5-0000-0000-0000-000000000001","folderKey":"c3d4e5f6-0000-0000-0000-000000000001","method":"POST","endpoint":"/issues"}'`,
|
|
2589
|
+
Output: {
|
|
2590
|
+
Code: "NodeConfigureSuccess",
|
|
2591
|
+
Data: {
|
|
2592
|
+
NodeId: "node_a1b2c3d4",
|
|
2593
|
+
BindingsCreated: 2,
|
|
2594
|
+
DetailPopulated: true
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
},
|
|
2598
|
+
{
|
|
2599
|
+
Description: "Configure a generic connector activity (e.g. SFDC InsertRecord) — requires objectName",
|
|
2600
|
+
Command: `uip maestro flow node configure flow.json node_a1b2c3d4 --detail '{"connectionId":"b2c3d4e5-0000-0000-0000-000000000001","folderKey":"c3d4e5f6-0000-0000-0000-000000000001","method":"POST","endpoint":"/Account","objectName":"Account","bodyParameters":{"Name":"Acme Corp"}}'`,
|
|
2601
|
+
Output: {
|
|
2602
|
+
Code: "NodeConfigureSuccess",
|
|
2603
|
+
Data: {
|
|
2604
|
+
NodeId: "node_a1b2c3d4",
|
|
2605
|
+
BindingsCreated: 2,
|
|
2606
|
+
DetailPopulated: true
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
},
|
|
2610
|
+
{
|
|
2611
|
+
Description: "Configure a param bound to another node's output — must use $vars., not the bare node id",
|
|
2612
|
+
Command: `uip maestro flow node configure flow.json node_a1b2c3d4 --detail '{"connectionId":"b2c3d4e5-0000-0000-0000-000000000001","folderKey":"c3d4e5f6-0000-0000-0000-000000000001","method":"POST","endpoint":"/Account","bodyParameters":{"recordId":"=js:$vars.createEntityRecord1.output.Id"}}'`,
|
|
2613
|
+
Output: {
|
|
2614
|
+
Code: "NodeConfigureSuccess",
|
|
2615
|
+
Data: {
|
|
2616
|
+
NodeId: "node_a1b2c3d4",
|
|
2617
|
+
BindingsCreated: 2,
|
|
2618
|
+
DetailPopulated: true
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
];
|
|
2623
|
+
var NODE_LIST_EXAMPLES = [
|
|
2624
|
+
{
|
|
2625
|
+
Description: "List all nodes in a flow file",
|
|
2626
|
+
Command: "uip maestro flow node list flow.json",
|
|
2627
|
+
Output: {
|
|
2628
|
+
Code: "NodeListSuccess",
|
|
2629
|
+
Data: {
|
|
2630
|
+
Nodes: [
|
|
2631
|
+
{ id: "start", type: "core.trigger.manual" },
|
|
2632
|
+
{ id: "node_a1b2c3d4", type: "core.action.http.v2" }
|
|
2633
|
+
],
|
|
2634
|
+
Count: 2
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
];
|
|
2639
|
+
var NODE_DELETE_EXAMPLES = [
|
|
2640
|
+
{
|
|
2641
|
+
Description: "Delete a node and its connections from a flow",
|
|
2642
|
+
Command: "uip maestro flow node delete flow.json node_a1b2c3d4",
|
|
2643
|
+
Output: {
|
|
2644
|
+
Code: "NodeDeleteSuccess",
|
|
2645
|
+
Data: {
|
|
2646
|
+
DeletedNode: {
|
|
2647
|
+
id: "node_a1b2c3d4",
|
|
2648
|
+
type: "core.action.http.v2"
|
|
2649
|
+
},
|
|
2650
|
+
EdgesRemoved: 1,
|
|
2651
|
+
BindingsRemoved: 0,
|
|
2652
|
+
DefinitionsRemoved: 0,
|
|
2653
|
+
VariablesRemoved: 0,
|
|
2654
|
+
VariableUpdatesRemoved: 0
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
];
|
|
2659
|
+
var NODE_REMOVE_EXAMPLES = [
|
|
2660
|
+
{
|
|
2661
|
+
Description: "Remove a node and its connections from a flow",
|
|
2662
|
+
Command: "uip maestro flow node remove flow.json node_a1b2c3d4",
|
|
2663
|
+
Output: {
|
|
2664
|
+
Code: "NodeRemoveSuccess",
|
|
2665
|
+
Data: {
|
|
2666
|
+
DeletedNode: {
|
|
2667
|
+
id: "node_a1b2c3d4",
|
|
2668
|
+
type: "core.action.http.v2"
|
|
2669
|
+
},
|
|
2670
|
+
EdgesRemoved: 1,
|
|
2671
|
+
BindingsRemoved: 0,
|
|
2672
|
+
DefinitionsRemoved: 0,
|
|
2673
|
+
VariablesRemoved: 0,
|
|
2674
|
+
VariableUpdatesRemoved: 0
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
];
|
|
2679
|
+
function parsePosition2(value) {
|
|
2680
|
+
const parts = value.split(",");
|
|
2681
|
+
if (parts.length !== 2) {
|
|
2682
|
+
throw new Error(`Invalid position format: "${value}". Expected "x,y" (e.g. 250,150)`);
|
|
2683
|
+
}
|
|
2684
|
+
const x = Number(parts[0]);
|
|
2685
|
+
const y = Number(parts[1]);
|
|
2686
|
+
if (Number.isNaN(x) || Number.isNaN(y)) {
|
|
2687
|
+
throw new Error(`Invalid position values: "${value}". x and y must be numbers`);
|
|
2688
|
+
}
|
|
2689
|
+
return { x, y };
|
|
2690
|
+
}
|
|
2691
|
+
function getDeleteNodeInstructions(file) {
|
|
2692
|
+
return `Check that "${file}" exists and run 'uip maestro flow node list ${file}' to find a valid node ID, then retry.`;
|
|
2693
|
+
}
|
|
2694
|
+
async function runDeleteNode(file, nodeId, code) {
|
|
2695
|
+
const { deleteNodeFromFlow } = await import("./node-service-8g5x5d2y.js");
|
|
2696
|
+
const normalizedNodeId = nodeId.trim();
|
|
2697
|
+
const [error, result] = await catchError(deleteNodeFromFlow(file, normalizedNodeId));
|
|
2698
|
+
if (error) {
|
|
2699
|
+
OutputFormatter.error({
|
|
2700
|
+
Result: RESULTS.Failure,
|
|
2701
|
+
Message: "Error deleting node",
|
|
2702
|
+
Instructions: getDeleteNodeInstructions(file)
|
|
2703
|
+
});
|
|
2704
|
+
processContext.exit(1);
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
OutputFormatter.success({
|
|
2708
|
+
Result: RESULTS.Success,
|
|
2709
|
+
Code: code,
|
|
2710
|
+
Data: {
|
|
2711
|
+
DeletedNode: result.deletedNode,
|
|
2712
|
+
EdgesRemoved: result.edgesRemoved,
|
|
2713
|
+
BindingsRemoved: result.bindingsRemoved,
|
|
2714
|
+
DefinitionsRemoved: result.definitionsRemoved,
|
|
2715
|
+
VariablesRemoved: result.variablesRemoved,
|
|
2716
|
+
VariableUpdatesRemoved: result.variableUpdatesRemoved
|
|
2717
|
+
}
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
var registerNodeCommand = (program) => {
|
|
2721
|
+
const node = program.command("node").description("Manage nodes in a Flow file");
|
|
2722
|
+
node.command("add").description(`Add a node to a Flow file.
|
|
2723
|
+
` + "For connector nodes (type uipath.connector), do NOT pass --input here — " + `run 'node configure' afterwards to supply connection and endpoint details.
|
|
2724
|
+
Example: uip maestro flow node add flow.flow uipath.connector.jira.createIssue --label 'Create Jira Issue'`).argument("<file>", "Path to the .flow file").argument("<node-type>", "Node type identifier (e.g. core.trigger.manual)").option("-i, --input <json>", `JSON object of input values to set on the node (e.g. '{"script": "..."}')`).option("--position <x,y>", "Node position in the canvas (e.g. 250,150)").option("--label <label>", "Display label for the node").option("--source <uuid>", "Source UUID for nodes whose manifest declares a source dependency. Use the agent's ProjectId (inline agent nodes) or the resource id from the parent inline agent's resources[] (agent-tool resource nodes). Populates inputs.source.").option("--parent <node-id>", "Id of the existing node that should contain this node. Set when authoring a loop body whose containment cannot be " + "inferred from edges alone — typically the id of a " + "core.logic.loop node.").examples(NODE_ADD_EXAMPLES).trackedAction(processContext, async (file, nodeType, options) => {
|
|
2725
|
+
let inputs;
|
|
2726
|
+
if (options.input !== undefined) {
|
|
2727
|
+
const [parseError, parsed] = catchError(() => JSON.parse(options.input));
|
|
2728
|
+
if (parseError) {
|
|
2729
|
+
OutputFormatter.error({
|
|
2730
|
+
Result: RESULTS.Failure,
|
|
2731
|
+
Message: "Invalid --input JSON",
|
|
2732
|
+
Instructions: parseError.message
|
|
2733
|
+
});
|
|
2734
|
+
processContext.exit(1);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2738
|
+
OutputFormatter.error({
|
|
2739
|
+
Result: RESULTS.Failure,
|
|
2740
|
+
Message: "Invalid --input JSON",
|
|
2741
|
+
Instructions: `--input must be a JSON object (e.g. '{"key": "value"}')`
|
|
2742
|
+
});
|
|
2743
|
+
processContext.exit(1);
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
inputs = parsed;
|
|
2747
|
+
}
|
|
2748
|
+
let position;
|
|
2749
|
+
if (options.position !== undefined) {
|
|
2750
|
+
const [posError, parsed] = catchError(() => parsePosition2(options.position));
|
|
2751
|
+
if (posError) {
|
|
2752
|
+
OutputFormatter.error({
|
|
2753
|
+
Result: RESULTS.Failure,
|
|
2754
|
+
Message: "Invalid --position value",
|
|
2755
|
+
Instructions: posError.message
|
|
2756
|
+
});
|
|
2757
|
+
processContext.exit(1);
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
position = parsed;
|
|
2761
|
+
}
|
|
2762
|
+
const {
|
|
2763
|
+
addNodeToFlow,
|
|
2764
|
+
NodeTypeNotEnabledError,
|
|
2765
|
+
NodeTypeNotFoundError,
|
|
2766
|
+
SourceRequiredError
|
|
2767
|
+
} = await import("./node-service-8g5x5d2y.js");
|
|
2768
|
+
const [error, result] = await catchError(addNodeToFlow(file, nodeType, {
|
|
2769
|
+
position,
|
|
2770
|
+
label: options.label,
|
|
2771
|
+
inputs,
|
|
2772
|
+
source: options.source,
|
|
2773
|
+
parent: options.parent
|
|
2774
|
+
}));
|
|
2775
|
+
if (error) {
|
|
2776
|
+
if (error instanceof NodeTypeNotEnabledError || error instanceof NodeTypeNotFoundError || error instanceof SourceRequiredError) {
|
|
2777
|
+
OutputFormatter.error({
|
|
2778
|
+
Result: RESULTS.Failure,
|
|
2779
|
+
Message: error.message,
|
|
2780
|
+
Instructions: error.instructions
|
|
2781
|
+
});
|
|
2782
|
+
processContext.exit(1);
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
OutputFormatter.error({
|
|
2786
|
+
Result: RESULTS.Failure,
|
|
2787
|
+
Message: "Error adding node",
|
|
2788
|
+
Instructions: error.message
|
|
2789
|
+
});
|
|
2790
|
+
processContext.exit(1);
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
OutputFormatter.success({
|
|
2794
|
+
Result: RESULTS.Success,
|
|
2795
|
+
Code: "NodeAddSuccess",
|
|
2796
|
+
Data: {
|
|
2797
|
+
Node: result.node,
|
|
2798
|
+
DefinitionAdded: result.definitionAdded,
|
|
2799
|
+
BindingsCreated: result.bindingsCreated,
|
|
2800
|
+
VariableCount: result.variableCount
|
|
2801
|
+
}
|
|
2802
|
+
});
|
|
2803
|
+
});
|
|
2804
|
+
node.command("configure").description(`Configure a connector or managed HTTP node with connection and method details.
|
|
2805
|
+
` + `Run this after 'node add' — populates inputs.detail and creates workflow bindings.
|
|
2806
|
+
` + `Use 'node list' to find the node ID.
|
|
2807
|
+
|
|
2808
|
+
CONNECTOR ACTIVITIES (type uipath.connector.*, NOT *.trigger.*)
|
|
2809
|
+
Required: connectionId, folderKey, method, endpoint.
|
|
2810
|
+
Generic activities (manifest activityType='Generic', e.g. *.insert-record,
|
|
2811
|
+
*.list-records) also require objectName. Discover with:
|
|
2812
|
+
uip is resources list <connector-key> --operation <op> --connection-id <uuid>
|
|
2813
|
+
uip is resources describe <connector-key> <object> --operation <op> --connection-id <uuid>
|
|
2814
|
+
Curated activities: get method/endpoint from connectorMethodInfo in 'registry get'.
|
|
2815
|
+
Optional: bodyParameters, queryParameters, pathParameters, filter,
|
|
2816
|
+
customFieldsRequestDetails ({ objectActionName, parameterValues: [[k,v],...] });
|
|
2817
|
+
Examples:
|
|
2818
|
+
--detail '{"connectionId":"<uuid>","folderKey":"<uuid>","method":"POST","endpoint":"/issues","bodyParameters":{"summary":"Bug"}}'
|
|
2819
|
+
--detail '{"connectionId":"<uuid>","folderKey":"<uuid>","method":"POST","endpoint":"/Account","objectName":"Account","bodyParameters":{"Name":"Acme"}}'
|
|
2820
|
+
Cross-node bindings (referencing another node's output) require the
|
|
2821
|
+
` + ` "$vars." prefix inside the "=js:" expression — the bare form
|
|
2822
|
+
` + ` (e.g. "=js:createEntityRecord1.output.Id") resolves to undefined at runtime:
|
|
2823
|
+
--detail '{"connectionId":"<uuid>","folderKey":"<uuid>","method":"POST","endpoint":"/Account","bodyParameters":{"recordId":"=js:$vars.createEntityRecord1.output.Id"}}'
|
|
2824
|
+
|
|
2825
|
+
CONNECTOR TRIGGERS (type uipath.connector.trigger.*)
|
|
2826
|
+
Required: connectionId, folderKey, eventMode (e.g. 'polling' or 'webhook').
|
|
2827
|
+
Generic triggers (manifest activityType='GenericTrigger', e.g. Data Fabric
|
|
2828
|
+
` + ` *.record-created) also require objectName — the object to subscribe to.
|
|
2829
|
+
` + ` Discover with: uip is triggers objects <connector-key> <eventType> --connection-id <uuid>
|
|
2830
|
+
Optional: eventParameters, queryParameters, pathParameters,
|
|
2831
|
+
filter (structured FilterTree, not a raw expression).
|
|
2832
|
+
Discover eventMode with:
|
|
2833
|
+
uip is triggers describe <connector-key> <eventType> <object> --connection-id <uuid>
|
|
2834
|
+
Examples:
|
|
2835
|
+
--detail '{"connectionId":"<uuid>","folderKey":"<uuid>","eventMode":"polling"}'
|
|
2836
|
+
--detail '{"connectionId":"<uuid>","folderKey":"<uuid>","eventMode":"webhooks","objectName":"MyEntity"}'
|
|
2837
|
+
|
|
2838
|
+
CONNECTOR WAIT-FOR EVENTS (type uipath.connector.event.*)
|
|
2839
|
+
Mid-flow Receive Task that pauses execution until the event fires.
|
|
2840
|
+
` + ` Same detail shape as connector triggers — only activityType differs internally.
|
|
2841
|
+
` + ` Required: connectionId, folderKey, eventMode (e.g. 'polling' or 'webhooks').
|
|
2842
|
+
Generic event nodes also require objectName (same rule as triggers).
|
|
2843
|
+
Optional: eventParameters, queryParameters, pathParameters,
|
|
2844
|
+
filter (structured FilterTree, not a raw expression).
|
|
2845
|
+
Discover eventMode with:
|
|
2846
|
+
uip is triggers describe <connector-key> <eventType> <object> --connection-id <uuid>
|
|
2847
|
+
Example:
|
|
2848
|
+
--detail '{"connectionId":"<uuid>","folderKey":"<uuid>","eventMode":"polling","queryParameters":{"repo":"cli"}}'
|
|
2849
|
+
|
|
2850
|
+
MANAGED HTTP NODES (type core.action.http.v2)
|
|
2851
|
+
` + ` Two authentication modes — set via the 'authentication' key:
|
|
2852
|
+
|
|
2853
|
+
` + ` Connector mode (authentication='connector'):
|
|
2854
|
+
Uses an Integration Service connection for auth.
|
|
2855
|
+
Required: authentication, method (GET/POST/PUT/PATCH/DELETE/OPTIONS/HEAD),
|
|
2856
|
+
connectionId (target connector connection UUID),
|
|
2857
|
+
folderKey, targetConnector (e.g. uipath-microsoft-outlook365)
|
|
2858
|
+
Optional: url (path appended to connector base URL), headers, query, body
|
|
2859
|
+
Example:
|
|
2860
|
+
--detail '{"authentication":"connector","targetConnector":"uipath-microsoft-outlook365","connectionId":"<uuid>","folderKey":"<uuid>","method":"GET","url":"/me"}'
|
|
2861
|
+
|
|
2862
|
+
Manual mode (authentication='manual'):
|
|
2863
|
+
` + ` No connector auth — provide the full URL and any auth headers yourself.
|
|
2864
|
+
` + ` Required: authentication, method, url (full URL)
|
|
2865
|
+
Optional: headers, query, body (string, can be expression e.g. '=js:({...})')
|
|
2866
|
+
Example:
|
|
2867
|
+
--detail '{"authentication":"manual","method":"GET","url":"https://api.example.com/data","headers":{"Authorization":"Bearer <token>"}}'`).argument("<file>", "Path to the .flow file").argument("<node-id>", "ID of the node to configure").option("--detail <json>", "Configuration as JSON (connectionId, folderKey, method, endpoint, objectName for generic activities, bodyParameters, etc.)").examples(NODE_CONFIGURE_EXAMPLES).trackedAction(processContext, async (file, nodeId, options) => {
|
|
2868
|
+
if (!options.detail) {
|
|
2869
|
+
OutputFormatter.error({
|
|
2870
|
+
Result: RESULTS.Failure,
|
|
2871
|
+
Message: "Missing --detail",
|
|
2872
|
+
Instructions: "--detail is required (JSON with connectionId, folderKey, etc.)"
|
|
2873
|
+
});
|
|
2874
|
+
processContext.exit(1);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
const [parseError, parsed] = catchError(() => JSON.parse(options.detail));
|
|
2878
|
+
if (parseError) {
|
|
2879
|
+
OutputFormatter.error({
|
|
2880
|
+
Result: RESULTS.Failure,
|
|
2881
|
+
Message: "Invalid --detail JSON",
|
|
2882
|
+
Instructions: parseError.message
|
|
2883
|
+
});
|
|
2884
|
+
processContext.exit(1);
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
const { configureConnectorNode, InvalidDetailError } = await import("./connector-service-d9ph9kx1.js");
|
|
2888
|
+
const [error, result] = await catchError(configureConnectorNode(file, nodeId, parsed));
|
|
2889
|
+
if (error) {
|
|
2890
|
+
const message = error instanceof InvalidDetailError ? "Invalid --detail" : "Error configuring node";
|
|
2891
|
+
OutputFormatter.error({
|
|
2892
|
+
Result: RESULTS.Failure,
|
|
2893
|
+
Message: message,
|
|
2894
|
+
Instructions: error.message
|
|
2895
|
+
});
|
|
2896
|
+
processContext.exit(1);
|
|
2897
|
+
return;
|
|
2898
|
+
}
|
|
2899
|
+
OutputFormatter.success({
|
|
2900
|
+
Result: RESULTS.Success,
|
|
2901
|
+
Code: "NodeConfigureSuccess",
|
|
2902
|
+
Data: {
|
|
2903
|
+
NodeId: nodeId,
|
|
2904
|
+
BindingsCreated: result.bindingsCreated,
|
|
2905
|
+
DetailPopulated: result.detailPopulated,
|
|
2906
|
+
...result.warnings && result.warnings.length > 0 && {
|
|
2907
|
+
Warnings: result.warnings
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
});
|
|
2911
|
+
});
|
|
2912
|
+
node.command("list").description("List all nodes in a Flow file").argument("<file>", "Path to the .flow file").examples(NODE_LIST_EXAMPLES).trackedAction(processContext, async (file) => {
|
|
2913
|
+
const { listNodesInFlow } = await import("./node-service-8g5x5d2y.js");
|
|
2914
|
+
const [error, result] = await catchError(listNodesInFlow(file));
|
|
2915
|
+
if (error) {
|
|
2916
|
+
OutputFormatter.error({
|
|
2917
|
+
Result: RESULTS.Failure,
|
|
2918
|
+
Message: "Error listing nodes",
|
|
2919
|
+
Instructions: error.message
|
|
2920
|
+
});
|
|
2921
|
+
processContext.exit(1);
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
OutputFormatter.success({
|
|
2925
|
+
Result: RESULTS.Success,
|
|
2926
|
+
Code: "NodeListSuccess",
|
|
2927
|
+
Data: {
|
|
2928
|
+
Nodes: result.nodes,
|
|
2929
|
+
Count: result.count
|
|
2930
|
+
}
|
|
2931
|
+
});
|
|
2932
|
+
});
|
|
2933
|
+
node.command("delete").description(`Delete a node from a Flow file.
|
|
2934
|
+
Automatically removes connected edges, orphaned bindings, orphaned definitions, and node variables owned by the deleted node.
|
|
2935
|
+
Use 'node list' to find the node ID.`).argument("<file>", "Path to the .flow file").argument("<node-id>", "ID of the node to delete").examples(NODE_DELETE_EXAMPLES).trackedAction(processContext, async (file, nodeId) => runDeleteNode(file, nodeId, "NodeDeleteSuccess"));
|
|
2936
|
+
node.command("remove").description(`Remove a node from a Flow file.
|
|
2937
|
+
Alias for 'node delete'. Automatically removes connected edges, orphaned bindings, orphaned definitions, and node variables owned by the removed node.
|
|
2938
|
+
Use 'node list' to find the node ID.`).argument("<file>", "Path to the .flow file").argument("<node-id>", "ID of the node to remove").examples(NODE_REMOVE_EXAMPLES).trackedAction(processContext, async (file, nodeId) => runDeleteNode(file, nodeId, "NodeRemoveSuccess"));
|
|
2939
|
+
};
|
|
2940
|
+
|
|
2941
|
+
// src/commands/pack.ts
|
|
2942
|
+
var FLOW_PACK_EXAMPLES = [
|
|
2943
|
+
{
|
|
2944
|
+
Description: "Pack a flow project into a .nupkg file",
|
|
2945
|
+
Command: "uip maestro flow pack ./my-flow-project ./dist --version 1.2.0",
|
|
2946
|
+
Output: {
|
|
2947
|
+
Code: "FlowPack",
|
|
2948
|
+
Data: {
|
|
2949
|
+
Package: "my-flow-project.1.2.0.nupkg",
|
|
2950
|
+
Output: "./dist/my-flow-project.1.2.0.nupkg"
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
];
|
|
2955
|
+
var registerPackCommand = (program) => {
|
|
2956
|
+
const command = program.command("pack").description("Pack a Flow project into a .nupkg file").argument("<project-path>", "Path to the Flow project directory").argument("<output-path>", "Output directory where the .nupkg will be saved").option("-n, --name <name>", "Package name (default: project folder name)").option("-v, --version <version>", "Package version (default: 1.0.0)", "1.0.0");
|
|
2957
|
+
registerPackageMetadataOptions(command).examples(FLOW_PACK_EXAMPLES).trackedAction(processContext, async (projectPath, outputPath, options) => {
|
|
2958
|
+
const { FlowPackService } = await import("./flow-pack-service-p4hxyj9e.js");
|
|
2959
|
+
const service = new FlowPackService;
|
|
2960
|
+
const exitCode = await service.execute(projectPath, {
|
|
2961
|
+
output: outputPath,
|
|
2962
|
+
name: options.name,
|
|
2963
|
+
version: options.version,
|
|
2964
|
+
...mapPackageMetadataOptions(options)
|
|
2965
|
+
});
|
|
2966
|
+
if (exitCode) {
|
|
2967
|
+
processContext.exit(exitCode);
|
|
2968
|
+
}
|
|
2969
|
+
});
|
|
2970
|
+
};
|
|
2971
|
+
|
|
2972
|
+
// src/commands/process.ts
|
|
2973
|
+
var FLOW_PROCESS_LIST_EXAMPLES = [
|
|
2974
|
+
{
|
|
2975
|
+
Description: "List available flow projects in a folder by key",
|
|
2976
|
+
Command: "uip maestro flow process list --folder-key c3d4e5f6-0000-0000-0000-000000000001",
|
|
2977
|
+
Output: {
|
|
2978
|
+
Code: "FlowProjectList",
|
|
2979
|
+
Data: [
|
|
2980
|
+
{
|
|
2981
|
+
name: "InvoiceFlow",
|
|
2982
|
+
processKey: "a1b2c3d4-0000-0000-0000-000000000001:1.0.0",
|
|
2983
|
+
releaseKey: "e5f6a7b8-0000-0000-0000-000000000001",
|
|
2984
|
+
folderKey: "c3d4e5f6-0000-0000-0000-000000000001",
|
|
2985
|
+
feedId: "default",
|
|
2986
|
+
folderId: 42,
|
|
2987
|
+
releaseActive: "Yes",
|
|
2988
|
+
releaseLatest: "Yes"
|
|
2989
|
+
}
|
|
2990
|
+
]
|
|
2991
|
+
}
|
|
2992
|
+
},
|
|
2993
|
+
{
|
|
2994
|
+
Description: "List available flow projects in a folder by path",
|
|
2995
|
+
Command: 'uip maestro flow process list --folder-path "Shared/Invoices"',
|
|
2996
|
+
Output: {
|
|
2997
|
+
Code: "FlowProjectList",
|
|
2998
|
+
Data: [
|
|
2999
|
+
{
|
|
3000
|
+
name: "InvoiceFlow",
|
|
3001
|
+
processKey: "a1b2c3d4-0000-0000-0000-000000000001:1.0.0",
|
|
3002
|
+
releaseKey: "e5f6a7b8-0000-0000-0000-000000000001",
|
|
3003
|
+
folderKey: "c3d4e5f6-0000-0000-0000-000000000001",
|
|
3004
|
+
feedId: "default",
|
|
3005
|
+
folderId: 42,
|
|
3006
|
+
active: "Yes",
|
|
3007
|
+
latest: "Yes"
|
|
3008
|
+
}
|
|
3009
|
+
]
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
];
|
|
3013
|
+
var FLOW_PROCESS_GET_EXAMPLES = [
|
|
3014
|
+
{
|
|
3015
|
+
Description: "Get a flow project's entry point schema",
|
|
3016
|
+
Command: "uip maestro flow process get a1b2c3d4-0000-0000-0000-000000000001 default --folder-key c3d4e5f6-0000-0000-0000-000000000001",
|
|
3017
|
+
Output: {
|
|
3018
|
+
Code: "FlowProjectGet",
|
|
3019
|
+
Data: [
|
|
3020
|
+
{
|
|
3021
|
+
Path: "/content/main.flow",
|
|
3022
|
+
DisplayName: "Main",
|
|
3023
|
+
Type: "Flow",
|
|
3024
|
+
InputSchema: '{"type":"object","properties":{}}',
|
|
3025
|
+
OutputSchema: '{"type":"object","properties":{}}'
|
|
3026
|
+
}
|
|
3027
|
+
]
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
];
|
|
3031
|
+
var FLOW_PROCESS_RUN_EXAMPLES = [
|
|
3032
|
+
{
|
|
3033
|
+
Description: "Run a flow project with JSON inputs",
|
|
3034
|
+
Command: `uip maestro flow process run a1b2c3d4-0000-0000-0000-000000000001 c3d4e5f6-0000-0000-0000-000000000001 --inputs '{"amount":100}'`,
|
|
3035
|
+
Output: {
|
|
3036
|
+
Code: "FlowJobStarted",
|
|
3037
|
+
Data: {
|
|
3038
|
+
jobKey: "b2c3d4e5-0000-0000-0000-000000000001",
|
|
3039
|
+
state: "Pending",
|
|
3040
|
+
traceId: "d4e5f6a7-0000-0000-0000-000000000001",
|
|
3041
|
+
folderKey: "c3d4e5f6-0000-0000-0000-000000000001"
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3045
|
+
{
|
|
3046
|
+
Description: "Run a flow project and bind a local file to a file-typed input variable",
|
|
3047
|
+
Command: "uip maestro flow process run a1b2c3d4-0000-0000-0000-000000000001 c3d4e5f6-0000-0000-0000-000000000001 --attachment file1=./resume.pdf",
|
|
3048
|
+
Output: {
|
|
3049
|
+
Code: "FlowJobStarted",
|
|
3050
|
+
Data: {
|
|
3051
|
+
jobKey: "b2c3d4e5-0000-0000-0000-000000000001",
|
|
3052
|
+
state: "Pending",
|
|
3053
|
+
traceId: "d4e5f6a7-0000-0000-0000-000000000001",
|
|
3054
|
+
folderKey: "c3d4e5f6-0000-0000-0000-000000000001"
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
];
|
|
3059
|
+
function flowProcessRunErrorCode(error) {
|
|
3060
|
+
return error.message.toLowerCase().includes("release key is required") ? "FlowReleaseKeyRequired" : undefined;
|
|
3061
|
+
}
|
|
3062
|
+
async function resolveReleaseKey(config, processKey) {
|
|
3063
|
+
const separator = processKey.lastIndexOf(":");
|
|
3064
|
+
const key = separator === -1 ? processKey : processKey.slice(0, separator);
|
|
3065
|
+
const version = separator === -1 ? undefined : processKey.slice(separator + 1);
|
|
3066
|
+
const { listFlowProcesses } = await import("./flow-api-wv8p8xbd.js");
|
|
3067
|
+
const processes = await listFlowProcesses(config);
|
|
3068
|
+
const matches = processes.filter((candidate) => candidate.processKey === key && (version === undefined || candidate.processVersion === version));
|
|
3069
|
+
const [releaseKey, ...extra] = matches.map((match) => match.key).filter((candidate) => Boolean(candidate));
|
|
3070
|
+
if (releaseKey === undefined) {
|
|
3071
|
+
throw new Error(`No Flow release matches process key '${processKey}' in folder ${config.folderKey}.`);
|
|
3072
|
+
}
|
|
3073
|
+
if (extra.length > 0) {
|
|
3074
|
+
throw new Error(`${matches.length} Flow releases match process key '${processKey}' in folder ${config.folderKey}: ${matches.map((match) => `${match.processVersion} (${match.key})`).join(", ")}.`);
|
|
3075
|
+
}
|
|
3076
|
+
return releaseKey;
|
|
3077
|
+
}
|
|
3078
|
+
async function resolveFolderForList(options) {
|
|
3079
|
+
const { resolveFolder } = await import("./index-ev4mdg5y.js");
|
|
3080
|
+
if (options.folderKey && options.folderPath) {
|
|
3081
|
+
const [pathError, pathFolder] = await catchError(resolveFolder({ folderPath: options.folderPath }, {
|
|
3082
|
+
tenant: options.tenant,
|
|
3083
|
+
loginValidity: options.loginValidity
|
|
3084
|
+
}));
|
|
3085
|
+
if (pathError || !pathFolder) {
|
|
3086
|
+
throw new Error(`Failed to resolve --folder-path '${options.folderPath}': ${pathError?.message ?? "folder not found"}.`);
|
|
3087
|
+
}
|
|
3088
|
+
if (options.folderKey.toLowerCase() !== pathFolder.key.toLowerCase()) {
|
|
3089
|
+
throw new Error(`--folder-key '${options.folderKey}' and --folder-path '${options.folderPath}' refer to different folders. Provide just one, or make them consistent.`);
|
|
3090
|
+
}
|
|
3091
|
+
return options.folderKey;
|
|
3092
|
+
}
|
|
3093
|
+
if (options.folderPath) {
|
|
3094
|
+
const [resolveError, resolved] = await catchError(resolveFolder({ folderPath: options.folderPath }, {
|
|
3095
|
+
tenant: options.tenant,
|
|
3096
|
+
loginValidity: options.loginValidity
|
|
3097
|
+
}));
|
|
3098
|
+
if (resolveError || !resolved) {
|
|
3099
|
+
throw new Error(`Failed to resolve --folder-path '${options.folderPath}': ${resolveError?.message ?? "folder not found"}.`);
|
|
3100
|
+
}
|
|
3101
|
+
return resolved.key;
|
|
3102
|
+
}
|
|
3103
|
+
if (!options.folderKey) {
|
|
3104
|
+
throw new Error("unreachable: folderKey must be defined when folderPath is absent");
|
|
3105
|
+
}
|
|
3106
|
+
return options.folderKey;
|
|
3107
|
+
}
|
|
3108
|
+
function registerProcessCommand(program) {
|
|
3109
|
+
const processCmd = program.command("process").description("Manage Flow processes");
|
|
3110
|
+
registerSharedProcessCommands(processCmd, { toolLabel: "Flow" });
|
|
3111
|
+
processCmd.command("list").description("List available Flow projects. The releaseActive and releaseLatest fields reflect Orchestrator release-level state (whether the release is marked active or the highest version on the manifest) and do not indicate whether the process is deployed or runnable.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--filter <odata>", "Additional OData filter").option("-f, --folder-key <key>", "Folder key (GUID)").option("--folder-path <path>", "Folder path (e.g. 'Shared/Invoices'). Alternative to --folder-key.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(FLOW_PROCESS_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
3112
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
3113
|
+
if (options.folderKey === undefined && options.folderPath === undefined) {
|
|
3114
|
+
OutputFormatter.error({
|
|
3115
|
+
Result: RESULTS.ValidationError,
|
|
3116
|
+
Message: "Missing required option --folder-key or --folder-path.",
|
|
3117
|
+
Instructions: "Provide --folder-key <key> or --folder-path <path> and try again."
|
|
3118
|
+
});
|
|
3119
|
+
processContext.exit(3);
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
3123
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
3124
|
+
}));
|
|
3125
|
+
if (loginError) {
|
|
3126
|
+
OutputFormatter.error({
|
|
3127
|
+
Result: RESULTS.Failure,
|
|
3128
|
+
Message: loginError.message,
|
|
3129
|
+
Instructions: instructionsFor("auth")
|
|
3130
|
+
});
|
|
3131
|
+
processContext.exit(1);
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
3135
|
+
OutputFormatter.error({
|
|
3136
|
+
Result: RESULTS.Failure,
|
|
3137
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
3138
|
+
Instructions: instructionsFor("auth")
|
|
3139
|
+
});
|
|
3140
|
+
processContext.exit(1);
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
const tenantName = options.tenant || loginStatus.tenantName;
|
|
3144
|
+
if (!tenantName) {
|
|
3145
|
+
OutputFormatter.error({
|
|
3146
|
+
Result: RESULTS.Failure,
|
|
3147
|
+
Message: "Tenant not provided and not available from login. Run 'uip login' or 'uip login tenant set <tenant>'.",
|
|
3148
|
+
Instructions: instructionsFor("identity")
|
|
3149
|
+
});
|
|
3150
|
+
processContext.exit(1);
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
if (!loginStatus.organizationId) {
|
|
3154
|
+
OutputFormatter.error({
|
|
3155
|
+
Result: RESULTS.Failure,
|
|
3156
|
+
Message: "Organization ID not available from login.",
|
|
3157
|
+
Instructions: instructionsFor("identity")
|
|
3158
|
+
});
|
|
3159
|
+
processContext.exit(1);
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
const [resolveError, resolvedFolderKey] = await catchError(resolveFolderForList(options));
|
|
3163
|
+
if (resolveError) {
|
|
3164
|
+
OutputFormatter.error({
|
|
3165
|
+
Result: RESULTS.ValidationError,
|
|
3166
|
+
Message: resolveError.message,
|
|
3167
|
+
Instructions: "Provide a valid --folder-key or --folder-path, then retry."
|
|
3168
|
+
});
|
|
3169
|
+
processContext.exit(3);
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
const config = {
|
|
3173
|
+
processKey: "",
|
|
3174
|
+
folderKey: resolvedFolderKey,
|
|
3175
|
+
inputs: {},
|
|
3176
|
+
authToken: loginStatus.accessToken || "",
|
|
3177
|
+
organizationId: loginStatus.organizationId,
|
|
3178
|
+
tenantName,
|
|
3179
|
+
baseUrl: loginStatus.baseUrl
|
|
3180
|
+
};
|
|
3181
|
+
const { listFlowProcesses } = await import("./flow-api-wv8p8xbd.js");
|
|
3182
|
+
const [listError, processes] = await catchError(listFlowProcesses(config, options.filter));
|
|
3183
|
+
if (listError) {
|
|
3184
|
+
OutputFormatter.error({
|
|
3185
|
+
Result: RESULTS.Failure,
|
|
3186
|
+
Message: listError.message,
|
|
3187
|
+
Instructions: instructionsFor("backend", listError)
|
|
3188
|
+
});
|
|
3189
|
+
processContext.exit(1);
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
const formattedData = processes.map((p) => ({
|
|
3193
|
+
name: p.name,
|
|
3194
|
+
processKey: `${p.processKey}:${p.processVersion}`,
|
|
3195
|
+
releaseKey: p.key,
|
|
3196
|
+
folderKey: p.folderKey,
|
|
3197
|
+
feedId: p.feedId || "",
|
|
3198
|
+
folderId: p.organizationUnitId || "",
|
|
3199
|
+
releaseActive: p.isActive ? "Yes" : "No",
|
|
3200
|
+
releaseLatest: p.isLatestVersion ? "Yes" : "No"
|
|
3201
|
+
}));
|
|
3202
|
+
OutputFormatter.emitList("FlowProjectList", formattedData, {
|
|
3203
|
+
emptyInstructions: "No Flow projects found. Check folder permissions or try a different folder."
|
|
3204
|
+
});
|
|
3205
|
+
});
|
|
3206
|
+
processCmd.command("get").description("Get Flow project schema and details").argument("<process-key>", "Process key (e.g., MyFlow.flow.Flow:1.0.0)").argument("<feed-id>", "Feed ID (from 'flow project list')").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).requiredOption("-f, --folder-key <key>", "Folder key (GUID)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(FLOW_PROCESS_GET_EXAMPLES).trackedAction(processContext, async (processKey, feedId, options) => {
|
|
3207
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
3208
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
3209
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
3210
|
+
}));
|
|
3211
|
+
if (loginError) {
|
|
3212
|
+
OutputFormatter.error({
|
|
3213
|
+
Result: RESULTS.Failure,
|
|
3214
|
+
Message: loginError.message,
|
|
3215
|
+
Instructions: instructionsFor("auth")
|
|
3216
|
+
});
|
|
3217
|
+
processContext.exit(1);
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
3221
|
+
OutputFormatter.error({
|
|
3222
|
+
Result: RESULTS.Failure,
|
|
3223
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
3224
|
+
Instructions: instructionsFor("auth")
|
|
3225
|
+
});
|
|
3226
|
+
processContext.exit(1);
|
|
3227
|
+
return;
|
|
3228
|
+
}
|
|
3229
|
+
const tenantName = options.tenant || loginStatus.tenantName;
|
|
3230
|
+
if (!tenantName) {
|
|
3231
|
+
OutputFormatter.error({
|
|
3232
|
+
Result: RESULTS.Failure,
|
|
3233
|
+
Message: "Tenant not provided and not available from login. Run 'uip login' or 'uip login tenant set <tenant>'.",
|
|
3234
|
+
Instructions: instructionsFor("identity")
|
|
3235
|
+
});
|
|
3236
|
+
processContext.exit(1);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
if (!loginStatus.organizationId) {
|
|
3240
|
+
OutputFormatter.error({
|
|
3241
|
+
Result: RESULTS.Failure,
|
|
3242
|
+
Message: "Organization ID not available from login.",
|
|
3243
|
+
Instructions: instructionsFor("identity")
|
|
3244
|
+
});
|
|
3245
|
+
processContext.exit(1);
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
const config = {
|
|
3249
|
+
processKey,
|
|
3250
|
+
folderKey: options.folderKey,
|
|
3251
|
+
inputs: {},
|
|
3252
|
+
authToken: loginStatus.accessToken || "",
|
|
3253
|
+
organizationId: loginStatus.organizationId,
|
|
3254
|
+
tenantName,
|
|
3255
|
+
baseUrl: loginStatus.baseUrl,
|
|
3256
|
+
feedId
|
|
3257
|
+
};
|
|
3258
|
+
const { getFlowEntryPoints } = await import("./flow-api-wv8p8xbd.js");
|
|
3259
|
+
const [entryPointsError, entryPoints] = await catchError(getFlowEntryPoints(config));
|
|
3260
|
+
if (entryPointsError) {
|
|
3261
|
+
OutputFormatter.error({
|
|
3262
|
+
Result: RESULTS.Failure,
|
|
3263
|
+
Message: entryPointsError.message,
|
|
3264
|
+
Instructions: instructionsFor("backend", entryPointsError)
|
|
3265
|
+
});
|
|
3266
|
+
processContext.exit(1);
|
|
3267
|
+
return;
|
|
3268
|
+
}
|
|
3269
|
+
if (entryPoints.length === 0) {
|
|
3270
|
+
OutputFormatter.error({
|
|
3271
|
+
Result: RESULTS.Failure,
|
|
3272
|
+
Message: "No entry points found for this process.",
|
|
3273
|
+
Instructions: "Verify the process key is correct and published."
|
|
3274
|
+
});
|
|
3275
|
+
processContext.exit(1);
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
const formattedData = entryPoints.map((ep) => {
|
|
3279
|
+
const [, inputSchema] = catchError(() => JSON.parse(ep.InputArguments));
|
|
3280
|
+
const [, outputSchema] = catchError(() => JSON.parse(ep.OutputArguments));
|
|
3281
|
+
return {
|
|
3282
|
+
Path: ep.Path,
|
|
3283
|
+
DisplayName: ep.DisplayName,
|
|
3284
|
+
Type: ep.Type,
|
|
3285
|
+
InputSchema: JSON.stringify(inputSchema ?? {}, null, 2),
|
|
3286
|
+
OutputSchema: JSON.stringify(outputSchema ?? {}, null, 2)
|
|
3287
|
+
};
|
|
3288
|
+
});
|
|
3289
|
+
OutputFormatter.success({
|
|
3290
|
+
Result: RESULTS.Success,
|
|
3291
|
+
Code: "FlowProjectGet",
|
|
3292
|
+
Data: formattedData,
|
|
3293
|
+
Instructions: `Found ${entryPoints.length} entry point(s) for ${processKey}`
|
|
3294
|
+
});
|
|
3295
|
+
});
|
|
3296
|
+
processCmd.command("run").description("Run a Flow project with inputs").argument("<process-key>", "Process key (e.g., MyFlow.flow.Flow:1.0.0)").argument("<folder-key>", "Folder key (GUID)").option("-i, --inputs <json>", "Input parameters as JSON string or @file.json").option("--attachment <name=path>", "Upload a local file as a file-typed workflow input variable (repeatable). Example: --attachment file1=./resume.pdf", appendOption, []).addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--release-key <key>", "Release key (GUID, from list command)").option("--feed-id <id>", "Feed ID for package lookup (optional)").option("--robot-ids <ids>", "Comma-separated robot IDs (optional)").option("--validate", "Validate inputs against project schema (basic: required fields + primitive types)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--wait", "Block until the job reaches a terminal state (Successful, Faulted, Stopped)").option("--timeout <seconds>", "Maximum seconds to wait (0 = no timeout). Requires --wait.", String(DEFAULT_TIMEOUT_MS / 1000)).option("--poll-interval <seconds>", "Seconds between status checks. Requires --wait.", String(DEFAULT_INTERVAL_MS / 1000)).examples(FLOW_PROCESS_RUN_EXAMPLES).trackedAction(processContext, async (processKey, folderKey, options) => {
|
|
3297
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
3298
|
+
const {
|
|
3299
|
+
parseFlowInputs,
|
|
3300
|
+
startFlowProjectJob,
|
|
3301
|
+
validateFlowInputs
|
|
3302
|
+
} = await import("./flow-api-wv8p8xbd.js");
|
|
3303
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
3304
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
3305
|
+
}));
|
|
3306
|
+
if (loginError) {
|
|
3307
|
+
OutputFormatter.error({
|
|
3308
|
+
Result: RESULTS.Failure,
|
|
3309
|
+
Message: loginError.message,
|
|
3310
|
+
Instructions: instructionsFor("auth")
|
|
3311
|
+
});
|
|
3312
|
+
processContext.exit(1);
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3315
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
3316
|
+
OutputFormatter.error({
|
|
3317
|
+
Result: RESULTS.Failure,
|
|
3318
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
3319
|
+
Instructions: instructionsFor("auth")
|
|
3320
|
+
});
|
|
3321
|
+
processContext.exit(1);
|
|
3322
|
+
return;
|
|
3323
|
+
}
|
|
3324
|
+
const tenantName = options.tenant || loginStatus.tenantName;
|
|
3325
|
+
if (!tenantName) {
|
|
3326
|
+
OutputFormatter.error({
|
|
3327
|
+
Result: RESULTS.Failure,
|
|
3328
|
+
Message: "Tenant not provided and not available from login. Run 'uip login' or 'uip login tenant set <tenant>'.",
|
|
3329
|
+
Instructions: instructionsFor("identity")
|
|
3330
|
+
});
|
|
3331
|
+
processContext.exit(1);
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
if (!loginStatus.organizationId) {
|
|
3335
|
+
OutputFormatter.error({
|
|
3336
|
+
Result: RESULTS.Failure,
|
|
3337
|
+
Message: "Organization ID not available from login.",
|
|
3338
|
+
Instructions: instructionsFor("identity")
|
|
3339
|
+
});
|
|
3340
|
+
processContext.exit(1);
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3343
|
+
let inputs = {};
|
|
3344
|
+
if (options.inputs) {
|
|
3345
|
+
const [parseError, parsed] = await catchError(parseFlowInputs(options.inputs));
|
|
3346
|
+
if (parseError) {
|
|
3347
|
+
OutputFormatter.error({
|
|
3348
|
+
Result: RESULTS.Failure,
|
|
3349
|
+
Message: parseError.message,
|
|
3350
|
+
Instructions: instructionsFor("input-parse")
|
|
3351
|
+
});
|
|
3352
|
+
processContext.exit(1);
|
|
3353
|
+
return;
|
|
3354
|
+
}
|
|
3355
|
+
inputs = parsed;
|
|
3356
|
+
} else {
|
|
3357
|
+
const { readStdin } = await import("./input-reader-6pwhtp8v.js");
|
|
3358
|
+
const [stdinError, stdinData] = await catchError(readStdin());
|
|
3359
|
+
if (stdinError) {
|
|
3360
|
+
OutputFormatter.error({
|
|
3361
|
+
Result: RESULTS.Failure,
|
|
3362
|
+
Message: stdinError.message,
|
|
3363
|
+
Instructions: instructionsFor("input-parse")
|
|
3364
|
+
});
|
|
3365
|
+
processContext.exit(1);
|
|
3366
|
+
return;
|
|
3367
|
+
}
|
|
3368
|
+
if (stdinData) {
|
|
3369
|
+
const [parseError, parsed] = await catchError(parseFlowInputs(stdinData));
|
|
3370
|
+
if (parseError) {
|
|
3371
|
+
OutputFormatter.error({
|
|
3372
|
+
Result: RESULTS.Failure,
|
|
3373
|
+
Message: parseError.message,
|
|
3374
|
+
Instructions: instructionsFor("input-parse")
|
|
3375
|
+
});
|
|
3376
|
+
processContext.exit(1);
|
|
3377
|
+
return;
|
|
3378
|
+
}
|
|
3379
|
+
inputs = parsed;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
const attachmentSpecs = options.attachment ?? [];
|
|
3383
|
+
if (attachmentSpecs.length > 0) {
|
|
3384
|
+
const [parseError, specs] = catchError(() => attachmentSpecs.map(parseAttachmentSpec));
|
|
3385
|
+
if (parseError) {
|
|
3386
|
+
OutputFormatter.error({
|
|
3387
|
+
Result: RESULTS.Failure,
|
|
3388
|
+
Message: parseError.message,
|
|
3389
|
+
Instructions: ATTACHMENT_INSTRUCTIONS
|
|
3390
|
+
});
|
|
3391
|
+
processContext.exit(1);
|
|
3392
|
+
return;
|
|
3393
|
+
}
|
|
3394
|
+
const { uploadJobAttachment } = await import("./index-ev4mdg5y.js");
|
|
3395
|
+
const [uploadError, resolved] = await catchError(resolveAttachmentInputs(specs, (bytes, fileName) => uploadJobAttachment(bytes, fileName, {
|
|
3396
|
+
tenant: tenantName
|
|
3397
|
+
})));
|
|
3398
|
+
if (uploadError) {
|
|
3399
|
+
OutputFormatter.error({
|
|
3400
|
+
Result: RESULTS.Failure,
|
|
3401
|
+
Message: uploadError.message,
|
|
3402
|
+
Instructions: ATTACHMENT_UPLOAD_INSTRUCTIONS
|
|
3403
|
+
});
|
|
3404
|
+
processContext.exit(1);
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
for (const { name, path, reference } of resolved) {
|
|
3408
|
+
if (name in inputs) {
|
|
3409
|
+
logger.warn(`--attachment '${name}' overrides the value provided in --inputs.`);
|
|
3410
|
+
}
|
|
3411
|
+
inputs[name] = reference;
|
|
3412
|
+
logger.info(`Uploaded '${path}' as attachment ${reference.ID} (variable '${name}').`);
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
const config = {
|
|
3416
|
+
processKey,
|
|
3417
|
+
folderKey,
|
|
3418
|
+
inputs,
|
|
3419
|
+
authToken: loginStatus.accessToken || "",
|
|
3420
|
+
organizationId: loginStatus.organizationId,
|
|
3421
|
+
tenantName,
|
|
3422
|
+
tenantId: loginStatus.tenantId,
|
|
3423
|
+
baseUrl: loginStatus.baseUrl,
|
|
3424
|
+
releaseKey: options.releaseKey,
|
|
3425
|
+
feedId: options.feedId
|
|
3426
|
+
};
|
|
3427
|
+
if (options.robotIds) {
|
|
3428
|
+
config.robotIds = options.robotIds.split(",").map((id) => parseInt(id.trim(), 10));
|
|
3429
|
+
if (config.robotIds.some((id) => Number.isNaN(id))) {
|
|
3430
|
+
OutputFormatter.error({
|
|
3431
|
+
Result: RESULTS.Failure,
|
|
3432
|
+
Message: `Invalid robot-ids: ${options.robotIds}. Must be comma-separated numbers.`,
|
|
3433
|
+
Instructions: instructionsFor("flag-format")
|
|
3434
|
+
});
|
|
3435
|
+
processContext.exit(1);
|
|
3436
|
+
return;
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
if (options.validate) {
|
|
3440
|
+
const [valError, validation] = await catchError(validateFlowInputs(config, inputs));
|
|
3441
|
+
if (valError) {
|
|
3442
|
+
const code = flowProcessRunErrorCode(valError);
|
|
3443
|
+
OutputFormatter.error({
|
|
3444
|
+
Result: RESULTS.Failure,
|
|
3445
|
+
...code ? { Code: code } : {},
|
|
3446
|
+
Message: valError.message,
|
|
3447
|
+
Instructions: instructionsFor("backend", valError)
|
|
3448
|
+
});
|
|
3449
|
+
processContext.exit(1);
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (!validation.valid) {
|
|
3453
|
+
const validationErrors = validation.errors ?? [];
|
|
3454
|
+
const errorList = validationErrors.length > 0 ? validationErrors.map((error) => `- ${error}`).join(`
|
|
3455
|
+
`) : "No validation details returned.";
|
|
3456
|
+
OutputFormatter.error({
|
|
3457
|
+
Result: RESULTS.Failure,
|
|
3458
|
+
Message: `Input validation failed:
|
|
3459
|
+
${errorList}`,
|
|
3460
|
+
Instructions: instructionsFor("input-validate")
|
|
3461
|
+
});
|
|
3462
|
+
processContext.exit(1);
|
|
3463
|
+
return;
|
|
3464
|
+
}
|
|
3465
|
+
logger.info("✅ Input validation passed");
|
|
3466
|
+
}
|
|
3467
|
+
let timeoutMs;
|
|
3468
|
+
let intervalMs;
|
|
3469
|
+
if (options.wait) {
|
|
3470
|
+
const timeoutS = Number(options.timeout);
|
|
3471
|
+
const intervalS = Number(options.pollInterval);
|
|
3472
|
+
if (!Number.isFinite(timeoutS) || timeoutS < 0) {
|
|
3473
|
+
OutputFormatter.error({
|
|
3474
|
+
Result: "Failure",
|
|
3475
|
+
Message: `Invalid --timeout value: '${options.timeout}'. Must be a non-negative number.`,
|
|
3476
|
+
Instructions: "Pass seconds, e.g. --timeout 300"
|
|
3477
|
+
});
|
|
3478
|
+
processContext.exit(1);
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
if (!Number.isFinite(intervalS) || intervalS <= 0) {
|
|
3482
|
+
OutputFormatter.error({
|
|
3483
|
+
Result: "Failure",
|
|
3484
|
+
Message: `Invalid --poll-interval value: '${options.pollInterval}'. Must be a positive number.`,
|
|
3485
|
+
Instructions: "Pass seconds, e.g. --poll-interval 10"
|
|
3486
|
+
});
|
|
3487
|
+
processContext.exit(1);
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
timeoutMs = timeoutS * 1000;
|
|
3491
|
+
intervalMs = intervalS * 1000;
|
|
3492
|
+
}
|
|
3493
|
+
if (config.releaseKey === undefined) {
|
|
3494
|
+
const [resolveError, resolvedReleaseKey] = await catchError(resolveReleaseKey(config, processKey));
|
|
3495
|
+
if (resolveError) {
|
|
3496
|
+
OutputFormatter.error({
|
|
3497
|
+
Result: RESULTS.Failure,
|
|
3498
|
+
Code: "FlowReleaseKeyRequired",
|
|
3499
|
+
Message: resolveError.message,
|
|
3500
|
+
Instructions: `Run 'uip maestro flow process list --folder-key ${folderKey}' and pass the matching releaseKey as --release-key.`
|
|
3501
|
+
});
|
|
3502
|
+
processContext.exit(1);
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
config.releaseKey = resolvedReleaseKey;
|
|
3506
|
+
logger.info(`Resolved --release-key ${resolvedReleaseKey} for process ${processKey}.`);
|
|
3507
|
+
}
|
|
3508
|
+
const [jobError, job] = await catchError(startFlowProjectJob(config));
|
|
3509
|
+
if (jobError) {
|
|
3510
|
+
const code = flowProcessRunErrorCode(jobError);
|
|
3511
|
+
OutputFormatter.error({
|
|
3512
|
+
Result: RESULTS.Failure,
|
|
3513
|
+
...code ? { Code: code } : {},
|
|
3514
|
+
Message: jobError.message,
|
|
3515
|
+
Instructions: instructionsFor("backend", jobError)
|
|
3516
|
+
});
|
|
3517
|
+
processContext.exit(1);
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
OutputFormatter.success({
|
|
3521
|
+
Result: RESULTS.Success,
|
|
3522
|
+
Code: "FlowJobStarted",
|
|
3523
|
+
Data: {
|
|
3524
|
+
jobKey: job.key,
|
|
3525
|
+
state: job.state,
|
|
3526
|
+
traceId: job.traceId,
|
|
3527
|
+
folderKey
|
|
3528
|
+
},
|
|
3529
|
+
Instructions: `Watch traces: uip maestro flow job traces ${job.key} --folder-key ${folderKey}`
|
|
3530
|
+
});
|
|
3531
|
+
if (!options.wait) {
|
|
3532
|
+
processContext.exit(0);
|
|
3533
|
+
return;
|
|
3534
|
+
}
|
|
3535
|
+
if (!job.key) {
|
|
3536
|
+
OutputFormatter.error({
|
|
3537
|
+
Result: "Failure",
|
|
3538
|
+
Message: "Job started but no key returned — cannot wait",
|
|
3539
|
+
Instructions: "Check the job in Orchestrator or retry without --wait."
|
|
3540
|
+
});
|
|
3541
|
+
processContext.exit(1);
|
|
3542
|
+
return;
|
|
3543
|
+
}
|
|
3544
|
+
const { handleFlowJobWait } = await import("./wait-for-flow-job-45p1a444.js");
|
|
3545
|
+
await handleFlowJobWait(job.key, {
|
|
3546
|
+
config,
|
|
3547
|
+
timeoutMs,
|
|
3548
|
+
intervalMs
|
|
3549
|
+
});
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// src/commands/processes.ts
|
|
3554
|
+
var registerProcessesCommand = (program) => {
|
|
3555
|
+
registerSharedProcessesCommands(program, {
|
|
3556
|
+
toolLabel: "Flow",
|
|
3557
|
+
processType: "Flow"
|
|
3558
|
+
});
|
|
3559
|
+
};
|
|
3560
|
+
|
|
3561
|
+
// src/commands/publish.ts
|
|
3562
|
+
var FLOW_PUBLISH_EXAMPLES = [
|
|
3563
|
+
{
|
|
3564
|
+
Description: "Pack and publish a Flow project to Orchestrator",
|
|
3565
|
+
Command: "uip maestro flow publish ./my-flow-project --version 1.2.0",
|
|
3566
|
+
Output: {
|
|
3567
|
+
Code: "FlowPublish",
|
|
3568
|
+
Data: {
|
|
3569
|
+
Package: "my-flow-project@1.2.0",
|
|
3570
|
+
PackagePath: "/tmp/flow-publish-1710000000000/my-flow-project.flow.Flow.1.2.0.nupkg",
|
|
3571
|
+
Upload: [
|
|
3572
|
+
{
|
|
3573
|
+
key: "my-flow-project.flow.Flow.1.2.0.nupkg",
|
|
3574
|
+
status: "Created"
|
|
3575
|
+
}
|
|
3576
|
+
]
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
];
|
|
3581
|
+
function getDefaultOutputPath(fs = getFileSystem()) {
|
|
3582
|
+
return fs.path.join(fs.env.tmpdir(), `flow-publish-${Date.now()}`);
|
|
3583
|
+
}
|
|
3584
|
+
var registerPublishCommand = (program) => {
|
|
3585
|
+
program.command("publish").description("Pack a Flow project and upload the resulting .nupkg to Orchestrator packages").argument("<project-path>", "Path to the Flow project directory").argument("[output-path]", "Output directory where the generated .nupkg will be saved").option("-n, --name <name>", "Package name (default: project folder name)").option("-v, --version <version>", "Package version (default: 1.0.0)", "1.0.0").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--feed-id <feedId>", "Feed ID (optional, defaults to tenant feed). Mutually exclusive with --folder-key/--folder-path.").option("--folder-key <key>", "Folder key (GUID) of an Orchestrator folder hierarchy feed. The CLI resolves the folder's feed ID automatically. Mutually exclusive with --feed-id and --folder-path.").option("--folder-path <path>", "Folder path (e.g. 'Shared/Sub') of an Orchestrator folder hierarchy feed. The CLI resolves the folder's feed ID automatically. Mutually exclusive with --feed-id and --folder-key.").examples(FLOW_PUBLISH_EXAMPLES).trackedAction(processContext, async (projectPath, outputPath, options) => {
|
|
3586
|
+
const { getLoginStatusAsync } = await import("./index-1kgxzh8x.js");
|
|
3587
|
+
const {
|
|
3588
|
+
createApiClient,
|
|
3589
|
+
PackageFeedsApi,
|
|
3590
|
+
ProcessesApi,
|
|
3591
|
+
resolveFolder
|
|
3592
|
+
} = await import("./index-ev4mdg5y.js");
|
|
3593
|
+
const fs = getFileSystem();
|
|
3594
|
+
const shouldCleanupOutput = outputPath === undefined;
|
|
3595
|
+
const packageOutputPath = outputPath ?? getDefaultOutputPath(fs);
|
|
3596
|
+
try {
|
|
3597
|
+
const [loginError, loginStatus] = await catchError(getLoginStatusAsync());
|
|
3598
|
+
if (loginError) {
|
|
3599
|
+
OutputFormatter.error({
|
|
3600
|
+
Result: RESULTS.Failure,
|
|
3601
|
+
Message: loginError.message,
|
|
3602
|
+
Instructions: instructionsFor("auth", loginError)
|
|
3603
|
+
});
|
|
3604
|
+
processContext.exit(1);
|
|
3605
|
+
return;
|
|
3606
|
+
}
|
|
3607
|
+
if (loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl) {
|
|
3608
|
+
OutputFormatter.error({
|
|
3609
|
+
Result: RESULTS.Failure,
|
|
3610
|
+
Message: "Not logged in. Run 'uip login' first.",
|
|
3611
|
+
Instructions: instructionsFor("auth")
|
|
3612
|
+
});
|
|
3613
|
+
processContext.exit(1);
|
|
3614
|
+
return;
|
|
3615
|
+
}
|
|
3616
|
+
const folderTargetingSpecified = options.folderKey !== undefined || options.folderPath !== undefined;
|
|
3617
|
+
if (folderTargetingSpecified && options.feedId) {
|
|
3618
|
+
OutputFormatter.error({
|
|
3619
|
+
Result: RESULTS.ValidationError,
|
|
3620
|
+
Message: "Cannot combine --feed-id with --folder-key/--folder-path.",
|
|
3621
|
+
Instructions: "Pass either --feed-id (raw feed) or --folder-key/--folder-path (the CLI resolves the feed) — not both."
|
|
3622
|
+
});
|
|
3623
|
+
processContext.exit(3);
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
if (options.folderKey !== undefined && options.folderPath !== undefined) {
|
|
3627
|
+
OutputFormatter.error({
|
|
3628
|
+
Result: RESULTS.ValidationError,
|
|
3629
|
+
Message: "Cannot combine --folder-key with --folder-path.",
|
|
3630
|
+
Instructions: "Pass either --folder-key or --folder-path to identify the folder feed — not both."
|
|
3631
|
+
});
|
|
3632
|
+
processContext.exit(3);
|
|
3633
|
+
return;
|
|
3634
|
+
}
|
|
3635
|
+
let resolvedFeedId = options.feedId;
|
|
3636
|
+
if (folderTargetingSpecified) {
|
|
3637
|
+
const [folderError, folder] = await catchError(resolveFolder({
|
|
3638
|
+
folderKey: options.folderKey,
|
|
3639
|
+
folderPath: options.folderPath
|
|
3640
|
+
}, { tenant: options.tenant }));
|
|
3641
|
+
if (folderError || !folder) {
|
|
3642
|
+
const message = folderError ? `Could not resolve folder: ${folderError.message}` : "Folder targeting requested but no folder resolved.";
|
|
3643
|
+
OutputFormatter.error({
|
|
3644
|
+
Result: RESULTS.ValidationError,
|
|
3645
|
+
Message: message,
|
|
3646
|
+
Instructions: "Provide a valid --folder-key or --folder-path. Use 'uip or folders list' to confirm the key or path."
|
|
3647
|
+
});
|
|
3648
|
+
processContext.exit(3);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
const [feedsApiError, feedsApi] = await catchError(createApiClient(PackageFeedsApi, {
|
|
3652
|
+
tenant: options.tenant
|
|
3653
|
+
}));
|
|
3654
|
+
if (feedsApiError) {
|
|
3655
|
+
OutputFormatter.error({
|
|
3656
|
+
Result: RESULTS.Failure,
|
|
3657
|
+
Message: "Flow publish failed",
|
|
3658
|
+
Instructions: "Could not create the feeds API client. Re-authenticate and verify the tenant name before trying again."
|
|
3659
|
+
});
|
|
3660
|
+
processContext.exit(1);
|
|
3661
|
+
return;
|
|
3662
|
+
}
|
|
3663
|
+
const [feedLookupError, folderFeedId] = await catchError(feedsApi.packageFeedsGetFolderFeed({
|
|
3664
|
+
folderId: folder.id
|
|
3665
|
+
}));
|
|
3666
|
+
if (feedLookupError || !folderFeedId) {
|
|
3667
|
+
const detail = feedLookupError ? await extractErrorMessage(feedLookupError) : "Folder has no associated package feed.";
|
|
3668
|
+
OutputFormatter.error({
|
|
3669
|
+
Result: RESULTS.Failure,
|
|
3670
|
+
Message: `Could not resolve folder feed for '${folder.path}': ${detail}`,
|
|
3671
|
+
Instructions: "Confirm the folder has FeedType 'FolderHierarchy' (see 'uip or folders get'). Tenant-scoped folders use the default tenant feed and don't need folder targeting."
|
|
3672
|
+
});
|
|
3673
|
+
processContext.exit(1);
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
resolvedFeedId = folderFeedId;
|
|
3677
|
+
}
|
|
3678
|
+
const { FlowPackService } = await import("./flow-pack-service-p4hxyj9e.js");
|
|
3679
|
+
const service = new FlowPackService;
|
|
3680
|
+
const [packError, packResult] = await catchError(service.executeAsync(projectPath, {
|
|
3681
|
+
output: packageOutputPath,
|
|
3682
|
+
name: options.name,
|
|
3683
|
+
version: options.version
|
|
3684
|
+
}));
|
|
3685
|
+
if (packError) {
|
|
3686
|
+
OutputFormatter.error({
|
|
3687
|
+
Result: RESULTS.Failure,
|
|
3688
|
+
Message: `Flow publish failed: ${packError.message}`,
|
|
3689
|
+
Instructions: "Fix the Flow project packaging error and run the command again."
|
|
3690
|
+
});
|
|
3691
|
+
processContext.exit(1);
|
|
3692
|
+
return;
|
|
3693
|
+
}
|
|
3694
|
+
if (!packResult.success || !packResult.outputPath) {
|
|
3695
|
+
OutputFormatter.error({
|
|
3696
|
+
Result: RESULTS.Failure,
|
|
3697
|
+
Message: `Flow publish failed: ${packResult.message || "Could not pack the Flow project."}`,
|
|
3698
|
+
Instructions: "Fix the Flow project packaging error and run the command again."
|
|
3699
|
+
});
|
|
3700
|
+
processContext.exit(1);
|
|
3701
|
+
return;
|
|
3702
|
+
}
|
|
3703
|
+
const [apiError, api] = await catchError(createApiClient(ProcessesApi, {
|
|
3704
|
+
tenant: options.tenant
|
|
3705
|
+
}));
|
|
3706
|
+
if (apiError) {
|
|
3707
|
+
OutputFormatter.error({
|
|
3708
|
+
Result: RESULTS.Failure,
|
|
3709
|
+
Message: "Flow publish failed",
|
|
3710
|
+
Instructions: "Authenticate with 'uip auth login' and verify the tenant name before trying again."
|
|
3711
|
+
});
|
|
3712
|
+
processContext.exit(1);
|
|
3713
|
+
return;
|
|
3714
|
+
}
|
|
3715
|
+
const [readError, fileBuffer] = await catchError(fs.readFile(packResult.outputPath));
|
|
3716
|
+
if (readError) {
|
|
3717
|
+
OutputFormatter.error({
|
|
3718
|
+
Result: RESULTS.Failure,
|
|
3719
|
+
Message: `Flow publish failed: ${readError.message}`,
|
|
3720
|
+
Instructions: `Verify the generated package exists at '${packResult.outputPath}' and that the current user can read it.`
|
|
3721
|
+
});
|
|
3722
|
+
processContext.exit(1);
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
const fileName = fs.path.basename(packResult.outputPath);
|
|
3726
|
+
const file = new File([fileBuffer], fileName, {
|
|
3727
|
+
type: "application/octet-stream"
|
|
3728
|
+
});
|
|
3729
|
+
const [uploadError, uploadResult] = await catchError(api.processesUploadPackage({
|
|
3730
|
+
file,
|
|
3731
|
+
feedId: resolvedFeedId
|
|
3732
|
+
}));
|
|
3733
|
+
if (uploadError) {
|
|
3734
|
+
const errorMessage = await extractErrorMessage(uploadError);
|
|
3735
|
+
OutputFormatter.error({
|
|
3736
|
+
Result: RESULTS.Failure,
|
|
3737
|
+
Message: `Flow package upload failed: ${errorMessage}`,
|
|
3738
|
+
Instructions: "Verify the tenant or feed ID, confirm you have package upload permissions, and retry with a new package version if the version already exists."
|
|
3739
|
+
});
|
|
3740
|
+
processContext.exit(1);
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
const shipped = packResult.packageName ? splitPackagedName(packResult.packageName) : undefined;
|
|
3744
|
+
trackShipSucceeded({
|
|
3745
|
+
ship_kind: "publish",
|
|
3746
|
+
target: folderTargetingSpecified ? "orchestrator_folder_package_feed" : resolvedFeedId ? "orchestrator_package_feed" : "orchestrator_tenant_package_feed",
|
|
3747
|
+
project_type: "flow",
|
|
3748
|
+
command_name: "uip.flow.publish",
|
|
3749
|
+
...shipped ? { package_name: shipped.name } : {},
|
|
3750
|
+
...shipped?.version ? { package_version: shipped.version } : {}
|
|
3751
|
+
});
|
|
3752
|
+
OutputFormatter.success({
|
|
3753
|
+
Result: RESULTS.Success,
|
|
3754
|
+
Code: "FlowPublish",
|
|
3755
|
+
Data: {
|
|
3756
|
+
Package: packResult.packageName ?? "",
|
|
3757
|
+
PackagePath: packResult.outputPath,
|
|
3758
|
+
Upload: uploadResult.value ?? []
|
|
3759
|
+
}
|
|
3760
|
+
});
|
|
3761
|
+
} finally {
|
|
3762
|
+
if (shouldCleanupOutput) {
|
|
3763
|
+
await catchError(fs.rm(packageOutputPath));
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
});
|
|
3767
|
+
};
|
|
3768
|
+
|
|
3769
|
+
// src/commands/registry.ts
|
|
3770
|
+
var registerRegistryCommand2 = (program) => {
|
|
3771
|
+
registerRegistryCommand(program, "maestro flow");
|
|
3772
|
+
};
|
|
3773
|
+
|
|
3774
|
+
// src/commands/tidy.ts
|
|
3775
|
+
var registerTidyCommand = (program) => {
|
|
3776
|
+
program.command("format").description("Format a .flow file by auto-laying out nodes to prevent overlaps and arrange in a clean flow").argument("<file>", "Path to the .flow file").trackedAction(processContext, async (file) => {
|
|
3777
|
+
const { tidyFlow } = await import("./tidy-service-ashgyfwj.js");
|
|
3778
|
+
const [error, result] = await catchError(tidyFlow(file));
|
|
3779
|
+
if (error) {
|
|
3780
|
+
OutputFormatter.error({
|
|
3781
|
+
Result: RESULTS.Failure,
|
|
3782
|
+
Message: "Failed to tidy flow",
|
|
3783
|
+
Instructions: error.message
|
|
3784
|
+
});
|
|
3785
|
+
processContext.exit(1);
|
|
3786
|
+
return;
|
|
3787
|
+
}
|
|
3788
|
+
const changes = [];
|
|
3789
|
+
if (result.nodesRepositioned > 0) {
|
|
3790
|
+
changes.push(`Repositioned ${result.nodesRepositioned} of ${result.nodeCount} nodes`);
|
|
3791
|
+
}
|
|
3792
|
+
if (result.nodesResized > 0) {
|
|
3793
|
+
changes.push(`Set canonical size on ${result.nodesResized} node(s)`);
|
|
3794
|
+
}
|
|
3795
|
+
if (result.subflowsTidied > 0) {
|
|
3796
|
+
changes.push(`Tidied ${result.subflowsTidied} subflow(s)`);
|
|
3797
|
+
}
|
|
3798
|
+
OutputFormatter.success({
|
|
3799
|
+
Result: RESULTS.Success,
|
|
3800
|
+
Code: "FlowFormat",
|
|
3801
|
+
Data: {
|
|
3802
|
+
File: file,
|
|
3803
|
+
NodesTotal: result.nodeCount,
|
|
3804
|
+
EdgesTotal: result.edgeCount,
|
|
3805
|
+
NodesRepositioned: result.nodesRepositioned,
|
|
3806
|
+
NodesResized: result.nodesResized,
|
|
3807
|
+
SubflowsTidied: result.subflowsTidied,
|
|
3808
|
+
VariableCount: result.variableCount
|
|
3809
|
+
},
|
|
3810
|
+
Instructions: changes.length > 0 ? changes.join(". ") : "All nodes already in optimal positions"
|
|
3811
|
+
});
|
|
3812
|
+
});
|
|
3813
|
+
};
|
|
3814
|
+
|
|
3815
|
+
// src/commands/validate.ts
|
|
3816
|
+
var STRICT_EXPRESSIONS_DEFAULT = false;
|
|
3817
|
+
var FLOW_VALIDATE_EXAMPLES = [
|
|
3818
|
+
{
|
|
3819
|
+
Description: "Validate a .flow file against the flow schema",
|
|
3820
|
+
Command: "uip maestro flow validate flow.json",
|
|
3821
|
+
Output: {
|
|
3822
|
+
Code: "FlowValidate",
|
|
3823
|
+
Data: {
|
|
3824
|
+
File: "flow.json",
|
|
3825
|
+
Status: "Valid"
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
},
|
|
3829
|
+
{
|
|
3830
|
+
Description: "Validate with governance policy checks (requires login)",
|
|
3831
|
+
Command: "uip maestro flow validate flow.json --governance",
|
|
3832
|
+
Output: {
|
|
3833
|
+
Code: "FlowValidate",
|
|
3834
|
+
Data: {
|
|
3835
|
+
File: "flow.json",
|
|
3836
|
+
Status: "Valid"
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
},
|
|
3840
|
+
{
|
|
3841
|
+
Description: "Treat connector-shaped Managed HTTP nodes without a binding as errors (CI-grade gate)",
|
|
3842
|
+
Command: "uip maestro flow validate flow.json --strict-bindings",
|
|
3843
|
+
Output: {
|
|
3844
|
+
Code: "FlowValidate",
|
|
3845
|
+
Data: {
|
|
3846
|
+
File: "flow.json",
|
|
3847
|
+
Status: "Valid"
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
},
|
|
3851
|
+
{
|
|
3852
|
+
Description: "Validate a project directory — resolves the project's .flow (project.uiproj MainFile)",
|
|
3853
|
+
Command: "uip maestro flow validate ./my-flow-project",
|
|
3854
|
+
Output: {
|
|
3855
|
+
Code: "FlowValidate",
|
|
3856
|
+
Data: {
|
|
3857
|
+
File: "my-flow-project/Main.flow",
|
|
3858
|
+
Status: "Valid"
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
},
|
|
3862
|
+
{
|
|
3863
|
+
Description: "Fail validation on expression and script type errors (they are warnings by default)",
|
|
3864
|
+
Command: "uip maestro flow validate flow.json --strict-expressions",
|
|
3865
|
+
Output: {
|
|
3866
|
+
Code: "FlowValidate",
|
|
3867
|
+
Data: {
|
|
3868
|
+
File: "flow.json",
|
|
3869
|
+
Status: "Valid"
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
];
|
|
3874
|
+
var registerValidateCommand = (program) => {
|
|
3875
|
+
program.command("validate").description("Validate a .flow file (or a project directory containing one) against the Flow schema").argument("<flowFile>", "Path to the .flow file (or a project directory containing one) to validate").option("--governance", "Validate agent nodes against organization governance policies (requires login)").option("--strict-bindings", "Fail validation when a Managed HTTP node (core.action.http.v2) carries a connector-shaped label but no Integration Service binding. Default: warn only.").option("--strict-expressions", "Fail validation when an expression or script body has a type error. The check always runs, using the shared Flow expression checker (the same engine the canvas runs); its findings are warnings by default. Default: warn only.").examples(FLOW_VALIDATE_EXAMPLES).trackedAction(processContext, async (flowFile, options) => {
|
|
3876
|
+
let governanceEnforcements;
|
|
3877
|
+
let availableModelNames;
|
|
3878
|
+
if (options.governance) {
|
|
3879
|
+
const { fetchGovernanceData } = await import("./governance-fetcher-j9cx4f1d.js");
|
|
3880
|
+
const [error, data] = await catchError(fetchGovernanceData());
|
|
3881
|
+
if (error) {
|
|
3882
|
+
OutputFormatter.error({
|
|
3883
|
+
Result: RESULTS.Failure,
|
|
3884
|
+
Message: `Could not fetch governance data: ${error.message}`,
|
|
3885
|
+
Instructions: "Ensure you are logged in and the platform is reachable, or omit --governance to skip policy checks."
|
|
3886
|
+
});
|
|
3887
|
+
processContext.exit(1);
|
|
3888
|
+
return;
|
|
3889
|
+
}
|
|
3890
|
+
governanceEnforcements = data.enforcements;
|
|
3891
|
+
availableModelNames = data.modelNames;
|
|
3892
|
+
}
|
|
3893
|
+
const { FlowValidateService } = await import("./flow-validate-service-j25pxpwz.js");
|
|
3894
|
+
const service = new FlowValidateService({
|
|
3895
|
+
governanceEnforcements,
|
|
3896
|
+
availableModelNames,
|
|
3897
|
+
validateCurrentManifests: true,
|
|
3898
|
+
strictBindings: options.strictBindings === true,
|
|
3899
|
+
strictExpressions: options.strictExpressions ?? STRICT_EXPRESSIONS_DEFAULT
|
|
3900
|
+
});
|
|
3901
|
+
const exitCode = await service.execute(flowFile);
|
|
3902
|
+
if (exitCode) {
|
|
3903
|
+
processContext.exit(exitCode);
|
|
3904
|
+
}
|
|
3905
|
+
});
|
|
3906
|
+
};
|
|
3907
|
+
|
|
3908
|
+
// src/commands/variable.ts
|
|
3909
|
+
var VARIABLE_ADD_EXAMPLES = [
|
|
3910
|
+
{
|
|
3911
|
+
Description: "Add a string input variable to a flow (auto-bound to the flow's trigger node, making it an entry-point input)",
|
|
3912
|
+
Command: "uip maestro flow variable add flow.json myInput --direction in --type string",
|
|
3913
|
+
Output: {
|
|
3914
|
+
Code: "VariableAddSuccess",
|
|
3915
|
+
Data: {
|
|
3916
|
+
Variable: {
|
|
3917
|
+
id: "myInput",
|
|
3918
|
+
direction: "in",
|
|
3919
|
+
type: "string",
|
|
3920
|
+
triggerNodeId: "manualTrigger1"
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
},
|
|
3925
|
+
{
|
|
3926
|
+
Description: "Bind an input to a specific trigger when the flow has several entry points",
|
|
3927
|
+
Command: "uip maestro flow variable add flow.json myInput --direction in --type string --trigger-node-id timerTrigger1",
|
|
3928
|
+
Output: {
|
|
3929
|
+
Code: "VariableAddSuccess",
|
|
3930
|
+
Data: {
|
|
3931
|
+
Variable: {
|
|
3932
|
+
id: "myInput",
|
|
3933
|
+
direction: "in",
|
|
3934
|
+
type: "string",
|
|
3935
|
+
triggerNodeId: "timerTrigger1"
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
];
|
|
3941
|
+
var VARIABLE_DELETE_EXAMPLES = [
|
|
3942
|
+
{
|
|
3943
|
+
Description: "Remove a variable from a flow",
|
|
3944
|
+
Command: "uip maestro flow variable remove flow.json myInput",
|
|
3945
|
+
Output: {
|
|
3946
|
+
Code: "VariableRemoveSuccess",
|
|
3947
|
+
Data: {
|
|
3948
|
+
DeletedVariable: {
|
|
3949
|
+
id: "myInput",
|
|
3950
|
+
direction: "in",
|
|
3951
|
+
type: "string"
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
];
|
|
3957
|
+
var VARIABLE_LIST_EXAMPLES = [
|
|
3958
|
+
{
|
|
3959
|
+
Description: "List all variables in a flow file",
|
|
3960
|
+
Command: "uip maestro flow variable list flow.json",
|
|
3961
|
+
Output: {
|
|
3962
|
+
Code: "VariableListSuccess",
|
|
3963
|
+
Data: {
|
|
3964
|
+
Variables: [
|
|
3965
|
+
{ id: "myInput", direction: "in", type: "string" },
|
|
3966
|
+
{ id: "result", direction: "out", type: "object" }
|
|
3967
|
+
],
|
|
3968
|
+
Count: 2
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
];
|
|
3973
|
+
var VALID_TYPES = ["string", "number", "boolean", "object", "array", "file"];
|
|
3974
|
+
var VALID_DIRECTIONS = ["in", "out", "inout"];
|
|
3975
|
+
function parseDefaultValue(value, type) {
|
|
3976
|
+
switch (type) {
|
|
3977
|
+
case "string":
|
|
3978
|
+
return { parsed: value };
|
|
3979
|
+
case "number": {
|
|
3980
|
+
const num = parseFloat(value);
|
|
3981
|
+
if (Number.isNaN(num)) {
|
|
3982
|
+
return {
|
|
3983
|
+
error: `Invalid default value for type 'number': '${value}'`
|
|
3984
|
+
};
|
|
3985
|
+
}
|
|
3986
|
+
return { parsed: num };
|
|
3987
|
+
}
|
|
3988
|
+
case "boolean": {
|
|
3989
|
+
if (value !== "true" && value !== "false") {
|
|
3990
|
+
return {
|
|
3991
|
+
error: `Invalid default value for type 'boolean': '${value}'. Must be 'true' or 'false'`
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
return { parsed: value === "true" };
|
|
3995
|
+
}
|
|
3996
|
+
case "object":
|
|
3997
|
+
case "array": {
|
|
3998
|
+
const [parseErr, parsedValue] = catchError(() => JSON.parse(value));
|
|
3999
|
+
if (parseErr) {
|
|
4000
|
+
return {
|
|
4001
|
+
error: `Invalid default value for type '${type}': must be valid JSON`
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
if (type === "object" && (typeof parsedValue !== "object" || parsedValue === null || Array.isArray(parsedValue))) {
|
|
4005
|
+
return {
|
|
4006
|
+
error: `Invalid default value for type 'object': must be a JSON object`
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
if (type === "array" && !Array.isArray(parsedValue)) {
|
|
4010
|
+
return {
|
|
4011
|
+
error: `Invalid default value for type 'array': must be a JSON array`
|
|
4012
|
+
};
|
|
4013
|
+
}
|
|
4014
|
+
return { parsed: parsedValue };
|
|
4015
|
+
}
|
|
4016
|
+
default:
|
|
4017
|
+
return { parsed: value };
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
var registerVariableCommand = (program) => {
|
|
4021
|
+
const variable = program.command("variable").description("Manage variables in a Flow file");
|
|
4022
|
+
variable.command("add").description("Add a variable to a Flow file").argument("<file>", "Path to the .flow file").argument("<variable-id>", "Variable identifier (e.g. myInputVar)").requiredOption("-d, --direction <direction>", `Variable direction (${VALID_DIRECTIONS.join(", ")})`).option("-t, --type <type>", `Data type (${VALID_TYPES.join(", ")})`, "string").option("--default-value <value>", "Default value (parsed based on --type)").option("--description <text>", "Human-readable description").option("--schema <json>", "JSON schema for object/array types").option("--sub-type <subType>", "Sub-type for complex types").option("--trigger-node-id <nodeId>", "Trigger node an input variable belongs to (defaults to the flow's only trigger node; required when the flow has several)").examples(VARIABLE_ADD_EXAMPLES).trackedAction(processContext, async (file, variableId, options) => {
|
|
4023
|
+
const { validateIdentifier } = await import("./index-4zrwgb3c.js");
|
|
4024
|
+
const identifierError = validateIdentifier(variableId, []);
|
|
4025
|
+
if (identifierError) {
|
|
4026
|
+
OutputFormatter.error({
|
|
4027
|
+
Result: RESULTS.Failure,
|
|
4028
|
+
Message: `Invalid variable id '${variableId}'`,
|
|
4029
|
+
Instructions: identifierError
|
|
4030
|
+
});
|
|
4031
|
+
processContext.exit(1);
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
if (!VALID_DIRECTIONS.includes(options.direction)) {
|
|
4035
|
+
OutputFormatter.error({
|
|
4036
|
+
Result: RESULTS.Failure,
|
|
4037
|
+
Message: `Invalid --direction '${options.direction}'`,
|
|
4038
|
+
Instructions: `Must be one of: ${VALID_DIRECTIONS.join(", ")}`
|
|
4039
|
+
});
|
|
4040
|
+
processContext.exit(1);
|
|
4041
|
+
return;
|
|
4042
|
+
}
|
|
4043
|
+
if (!VALID_TYPES.includes(options.type)) {
|
|
4044
|
+
OutputFormatter.error({
|
|
4045
|
+
Result: RESULTS.Failure,
|
|
4046
|
+
Message: `Invalid --type '${options.type}'`,
|
|
4047
|
+
Instructions: `Must be one of: ${VALID_TYPES.join(", ")}`
|
|
4048
|
+
});
|
|
4049
|
+
processContext.exit(1);
|
|
4050
|
+
return;
|
|
4051
|
+
}
|
|
4052
|
+
let defaultValue;
|
|
4053
|
+
if (options.defaultValue !== undefined) {
|
|
4054
|
+
const result = parseDefaultValue(options.defaultValue, options.type);
|
|
4055
|
+
if ("error" in result) {
|
|
4056
|
+
OutputFormatter.error({
|
|
4057
|
+
Result: RESULTS.Failure,
|
|
4058
|
+
Message: result.error,
|
|
4059
|
+
Instructions: "Provide a valid default value for the specified type"
|
|
4060
|
+
});
|
|
4061
|
+
processContext.exit(1);
|
|
4062
|
+
return;
|
|
4063
|
+
}
|
|
4064
|
+
defaultValue = result.parsed;
|
|
4065
|
+
}
|
|
4066
|
+
let schema;
|
|
4067
|
+
if (options.schema !== undefined) {
|
|
4068
|
+
const [parseError, parsed] = catchError(() => JSON.parse(options.schema));
|
|
4069
|
+
if (parseError) {
|
|
4070
|
+
OutputFormatter.error({
|
|
4071
|
+
Result: RESULTS.Failure,
|
|
4072
|
+
Message: "Invalid --schema JSON",
|
|
4073
|
+
Instructions: parseError.message
|
|
4074
|
+
});
|
|
4075
|
+
processContext.exit(1);
|
|
4076
|
+
return;
|
|
4077
|
+
}
|
|
4078
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
4079
|
+
OutputFormatter.error({
|
|
4080
|
+
Result: RESULTS.Failure,
|
|
4081
|
+
Message: "Invalid --schema value",
|
|
4082
|
+
Instructions: `--schema must be a JSON object (e.g. '{"type": "string"}')`
|
|
4083
|
+
});
|
|
4084
|
+
processContext.exit(1);
|
|
4085
|
+
return;
|
|
4086
|
+
}
|
|
4087
|
+
schema = parsed;
|
|
4088
|
+
}
|
|
4089
|
+
const { addVariableToFlow } = await import("./variable-service-er7ph4br.js");
|
|
4090
|
+
const [error, variable2] = await catchError(addVariableToFlow(file, {
|
|
4091
|
+
id: variableId,
|
|
4092
|
+
direction: options.direction,
|
|
4093
|
+
type: options.type,
|
|
4094
|
+
subType: options.subType,
|
|
4095
|
+
defaultValue,
|
|
4096
|
+
description: options.description,
|
|
4097
|
+
schema,
|
|
4098
|
+
triggerNodeId: options.triggerNodeId
|
|
4099
|
+
}));
|
|
4100
|
+
if (error) {
|
|
4101
|
+
OutputFormatter.error({
|
|
4102
|
+
Result: RESULTS.Failure,
|
|
4103
|
+
Message: "Error adding variable",
|
|
4104
|
+
Instructions: error.message
|
|
4105
|
+
});
|
|
4106
|
+
processContext.exit(1);
|
|
4107
|
+
return;
|
|
4108
|
+
}
|
|
4109
|
+
OutputFormatter.success({
|
|
4110
|
+
Result: RESULTS.Success,
|
|
4111
|
+
Code: "VariableAddSuccess",
|
|
4112
|
+
Data: {
|
|
4113
|
+
Variable: variable2
|
|
4114
|
+
}
|
|
4115
|
+
});
|
|
4116
|
+
});
|
|
4117
|
+
variable.command("remove").description("Remove a variable from a Flow file").argument("<file>", "Path to the .flow file").argument("<variable-id>", "Variable identifier to remove").examples(VARIABLE_DELETE_EXAMPLES).trackedAction(processContext, async (file, variableId) => {
|
|
4118
|
+
const { validateIdentifier } = await import("./index-4zrwgb3c.js");
|
|
4119
|
+
const identifierError = validateIdentifier(variableId, []);
|
|
4120
|
+
if (identifierError) {
|
|
4121
|
+
OutputFormatter.error({
|
|
4122
|
+
Result: RESULTS.Failure,
|
|
4123
|
+
Message: `Invalid variable id '${variableId}'`,
|
|
4124
|
+
Instructions: identifierError
|
|
4125
|
+
});
|
|
4126
|
+
processContext.exit(1);
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
const { deleteVariableFromFlow } = await import("./variable-service-er7ph4br.js");
|
|
4130
|
+
const [error, deletedVariable] = await catchError(deleteVariableFromFlow(file, variableId));
|
|
4131
|
+
if (error) {
|
|
4132
|
+
OutputFormatter.error({
|
|
4133
|
+
Result: RESULTS.Failure,
|
|
4134
|
+
Message: "Error deleting variable",
|
|
4135
|
+
Instructions: error.message
|
|
4136
|
+
});
|
|
4137
|
+
processContext.exit(1);
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
4140
|
+
OutputFormatter.success({
|
|
4141
|
+
Result: RESULTS.Success,
|
|
4142
|
+
Code: "VariableRemoveSuccess",
|
|
4143
|
+
Data: {
|
|
4144
|
+
DeletedVariable: deletedVariable
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
});
|
|
4148
|
+
variable.command("list").description("List all variables in a Flow file").argument("<file>", "Path to the .flow file").examples(VARIABLE_LIST_EXAMPLES).trackedAction(processContext, async (file) => {
|
|
4149
|
+
const { listVariablesInFlow } = await import("./variable-service-er7ph4br.js");
|
|
4150
|
+
const [error, result] = await catchError(listVariablesInFlow(file));
|
|
4151
|
+
if (error) {
|
|
4152
|
+
OutputFormatter.error({
|
|
4153
|
+
Result: RESULTS.Failure,
|
|
4154
|
+
Message: "Error listing variables",
|
|
4155
|
+
Instructions: error.message
|
|
4156
|
+
});
|
|
4157
|
+
processContext.exit(1);
|
|
4158
|
+
return;
|
|
4159
|
+
}
|
|
4160
|
+
OutputFormatter.success({
|
|
4161
|
+
Result: RESULTS.Success,
|
|
4162
|
+
Code: "VariableListSuccess",
|
|
4163
|
+
Data: {
|
|
4164
|
+
Variables: result.variables,
|
|
4165
|
+
Count: result.count
|
|
4166
|
+
}
|
|
4167
|
+
});
|
|
4168
|
+
});
|
|
4169
|
+
};
|
|
4170
|
+
|
|
4171
|
+
// src/services/project-metadata-reader.ts
|
|
4172
|
+
async function readAgentProjectMetadata(projectDir) {
|
|
4173
|
+
const fs = getFileSystem();
|
|
4174
|
+
const [readError, content] = await catchError(fs.readFile(fs.path.join(projectDir, "agent.json"), "utf-8"));
|
|
4175
|
+
if (readError || typeof content !== "string") {
|
|
4176
|
+
logger.debug(`No readable agent.json in ${projectDir}; the node will describe an autonomous agent.`);
|
|
4177
|
+
return {};
|
|
4178
|
+
}
|
|
4179
|
+
const [parseError, parsed] = catchError(() => JSON.parse(content));
|
|
4180
|
+
if (parseError) {
|
|
4181
|
+
logger.debug(`Unparseable agent.json in ${projectDir}: ${parseError.message}. The node will describe an autonomous agent.`);
|
|
4182
|
+
return {};
|
|
4183
|
+
}
|
|
4184
|
+
const metadata = {};
|
|
4185
|
+
const isConversational = parsed.metadata?.isConversational;
|
|
4186
|
+
if (typeof isConversational === "boolean") {
|
|
4187
|
+
metadata.isConversational = isConversational;
|
|
4188
|
+
}
|
|
4189
|
+
const targetRuntime = parsed.metadata?.targetRuntime;
|
|
4190
|
+
if (typeof targetRuntime === "string") {
|
|
4191
|
+
metadata.targetRuntime = targetRuntime;
|
|
4192
|
+
}
|
|
4193
|
+
return metadata;
|
|
4194
|
+
}
|
|
4195
|
+
var agentProjectMetadataReader = (projectDir, projectType) => projectType === "Agent" ? readAgentProjectMetadata(projectDir) : Promise.resolve({});
|
|
4196
|
+
|
|
4197
|
+
// src/tool.ts
|
|
4198
|
+
var metadata = {
|
|
4199
|
+
name: "flow-tool",
|
|
4200
|
+
version: package_default.version,
|
|
4201
|
+
description: "Create, debug, and run UiPath Flow projects and jobs.",
|
|
4202
|
+
commandPrefix: "flow"
|
|
4203
|
+
};
|
|
4204
|
+
var registerCommands = async (program) => {
|
|
4205
|
+
setAgentProjectMetadataReader(agentProjectMetadataReader);
|
|
4206
|
+
if (isPreviewBuild()) {
|
|
4207
|
+
const { registerFlowAuthoringCommands } = await import("./authoring-wpa6d0vf.js");
|
|
4208
|
+
registerFlowAuthoringCommands(program);
|
|
4209
|
+
}
|
|
4210
|
+
registerInitCommand(program);
|
|
4211
|
+
registerPackCommand(program);
|
|
4212
|
+
registerPublishCommand(program);
|
|
4213
|
+
registerValidateCommand(program);
|
|
4214
|
+
registerDebugCommand(program);
|
|
4215
|
+
registerDebugInstancesCommand(program);
|
|
4216
|
+
registerEvalCommand(program);
|
|
4217
|
+
registerMigrateCommand(program);
|
|
4218
|
+
registerNodeCommand(program);
|
|
4219
|
+
registerEdgeCommand(program);
|
|
4220
|
+
registerBindingCommand(program);
|
|
4221
|
+
registerProcessCommand(program);
|
|
4222
|
+
registerJobCommand(program);
|
|
4223
|
+
registerRegistryCommand2(program);
|
|
4224
|
+
registerInstancesCommand(program);
|
|
4225
|
+
registerProcessesCommand(program);
|
|
4226
|
+
registerHitlCommand(program);
|
|
4227
|
+
registerIncidentsCommand(program);
|
|
4228
|
+
registerTidyCommand(program);
|
|
4229
|
+
registerVariableCommand(program);
|
|
4230
|
+
};
|
|
4231
|
+
|
|
4232
|
+
export { metadata, registerCommands };
|
|
4233
|
+
|
|
4234
|
+
//# debugId=FE9DC859F1DB521864756E2164756E21
|