@maycombhq/atticus 2.0.0-beta.2 → 2.0.0-beta.21
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/bin.js +8 -4
- package/dist/chunk-265LWMOA.js +91 -0
- package/dist/chunk-26WBY34Y.js +37 -0
- package/dist/{chunk-UBBC2XSG.js → chunk-26Z7OGQW.js} +518 -93
- package/dist/chunk-2D7PWSOK.js +172 -0
- package/dist/{chunk-JCZIKA4C.js → chunk-2HH3UCZM.js} +4 -4
- package/dist/{chunk-GDLULHTC.js → chunk-2HTEMYTB.js} +12 -26
- package/dist/chunk-2UCRFXFZ.js +130 -0
- package/dist/{chunk-MVZVGYXH.js → chunk-2V2ZPQQU.js} +81 -13
- package/dist/{chunk-BOHJZTEQ.js → chunk-36RY7ZXM.js} +130 -71
- package/dist/{chunk-I3E7CX4H.js → chunk-37WE3LS2.js} +3 -3
- package/dist/{chunk-IPKHE5D6.js → chunk-3AQ2R5NL.js} +1520 -871
- package/dist/chunk-3G6S7LAA.js +43 -0
- package/dist/chunk-3J5I7ZOU.js +58 -0
- package/dist/{chunk-65NYPGRC.js → chunk-3LZ6P5EP.js} +1 -1
- package/dist/chunk-3SSTNB4E.js +191 -0
- package/dist/chunk-3TD6S4TC.js +49 -0
- package/dist/{chunk-3C7EYRFZ.js → chunk-3UTYPHN3.js} +1 -1
- package/dist/{chunk-G2B4ZQGH.js → chunk-3WON4G3M.js} +2 -2
- package/dist/{chunk-S3QNRWYR.js → chunk-42EPP7OB.js} +12 -12
- package/dist/chunk-455SYIIE.js +113 -0
- package/dist/{chunk-YISXLPHN.js → chunk-4B6BT75O.js} +166 -23
- package/dist/{chunk-C6EKFH7J.js → chunk-4CLK2RJO.js} +7 -7
- package/dist/chunk-4CYTJ5TZ.js +38 -0
- package/dist/{chunk-EYWIDJFU.js → chunk-4D62IZII.js} +5 -5
- package/dist/chunk-4FOAYLI4.js +328 -0
- package/dist/{chunk-7JUKDRSZ.js → chunk-4KNOJC6J.js} +294 -215
- package/dist/{chunk-YMZ3TNYB.js → chunk-4PYC2UGC.js} +2 -2
- package/dist/{chunk-27NKUS3X.js → chunk-4SLBCEPD.js} +2104 -726
- package/dist/chunk-4TKVU2I2.js +122 -0
- package/dist/{chunk-RUVFXE2L.js → chunk-4WXRPQDE.js} +448 -47
- package/dist/{chunk-7ZUIMTOU.js → chunk-5CMPVC3N.js} +24 -24
- package/dist/chunk-5G7ILBUG.js +66 -0
- package/dist/{chunk-HFQCFSJR.js → chunk-5PV7S2ZP.js} +3 -3
- package/dist/{chunk-FITWGOJ2.js → chunk-5QUOPNLN.js} +4 -4
- package/dist/{chunk-Z6LF7HQN.js → chunk-5TDRBOAV.js} +5 -5
- package/dist/{chunk-T6AIP7H5.js → chunk-5WERBKLE.js} +199 -63
- package/dist/{chunk-XPCM366L.js → chunk-62DLJSLV.js} +8 -8
- package/dist/{chunk-L24C55VN.js → chunk-64AADMTW.js} +99 -25
- package/dist/{chunk-K65BJIMT.js → chunk-67BP7HXL.js} +17 -17
- package/dist/chunk-6DQYZU2W.js +379 -0
- package/dist/{chunk-EDS2L2LD.js → chunk-6EXDI5GI.js} +52 -26
- package/dist/chunk-6FNAKCET.js +38 -0
- package/dist/{chunk-6IU67AEK.js → chunk-75ASLHBT.js} +21 -15
- package/dist/{chunk-A3GY6SOA.js → chunk-75ZOZFGH.js} +18 -18
- package/dist/{chunk-MGYZM27B.js → chunk-77O633L5.js} +49 -23
- package/dist/{chunk-GQ4N4WKF.js → chunk-7AB6OWTV.js} +3 -3
- package/dist/{chunk-M766ZJ6W.js → chunk-7CZHUZBL.js} +25 -25
- package/dist/{chunk-A7UPUQFY.js → chunk-7FHNFCHK.js} +643 -124
- package/dist/chunk-7FUIVIBG.js +415 -0
- package/dist/{chunk-6QSERWXH.js → chunk-7QPCGTPS.js} +21 -21
- package/dist/{chunk-LR3R6Z2A.js → chunk-A2G72UI4.js} +7 -7
- package/dist/{chunk-I4JRCD6M.js → chunk-AE4P57KK.js} +23 -17
- package/dist/{chunk-AVJDSJ2F.js → chunk-AHW3RY3X.js} +7 -7
- package/dist/chunk-AM6FP6JW.js +43 -0
- package/dist/{chunk-CSZ4C5N6.js → chunk-AOCWFA7T.js} +93 -10
- package/dist/{chunk-EPTBZ3GP.js → chunk-API5TOUB.js} +1 -1
- package/dist/{chunk-652QWTLZ.js → chunk-AZDITBCT.js} +3 -3
- package/dist/{chunk-DPUYDDQG.js → chunk-BA2HUMDN.js} +25 -3
- package/dist/chunk-BA3U7ALS.js +99 -0
- package/dist/{chunk-HVUPKCGJ.js → chunk-BC6KKZ67.js} +5 -5
- package/dist/{chunk-CKSITVS4.js → chunk-BEPCKDCP.js} +2554 -722
- package/dist/{chunk-3ALD2W2A.js → chunk-BPPU3PW7.js} +4 -4
- package/dist/{chunk-QRAOANID.js → chunk-BVWWPBKD.js} +21 -21
- package/dist/{chunk-ZDPAS56H.js → chunk-BXBCMGIQ.js} +7 -1
- package/dist/chunk-BXZ4PKIT.js +233 -0
- package/dist/chunk-C7CEK3KW.js +247 -0
- package/dist/chunk-CFH3QH26.js +29 -0
- package/dist/chunk-CG6CULP6.js +170 -0
- package/dist/chunk-CLD4RVTA.js +45 -0
- package/dist/{chunk-DKJA76CJ.js → chunk-CNPY4NTZ.js} +13 -13
- package/dist/chunk-CNU5Q2D5.js +575 -0
- package/dist/{chunk-T2UYZSCQ.js → chunk-COYN3UAN.js} +126 -23
- package/dist/{chunk-23W7VWKR.js → chunk-CPK7QSBG.js} +1360 -113
- package/dist/{chunk-4AK7IBVP.js → chunk-D2WSW4GU.js} +1 -1
- package/dist/{chunk-ABWHYWNI.js → chunk-D57MFJPP.js} +3 -3
- package/dist/chunk-D5KV5PSK.js +45 -0
- package/dist/chunk-D5MASS4I.js +48 -0
- package/dist/{chunk-7EOBHUMC.js → chunk-DCWN3HFV.js} +2 -2
- package/dist/{chunk-QSXK2RGH.js → chunk-DDJPM5YX.js} +2 -2
- package/dist/chunk-DN4A2Q2O.js +57 -0
- package/dist/{chunk-IHMLFRVV.js → chunk-DUPZPDCJ.js} +58 -16
- package/dist/{chunk-PRPPGYZG.js → chunk-E2VXBSIR.js} +4 -4
- package/dist/chunk-E57AOFYN.js +32 -0
- package/dist/{chunk-UGLN7QHV.js → chunk-ECLXZWAG.js} +12 -12
- package/dist/{chunk-5WG5YP4T.js → chunk-EDZ45WIR.js} +14 -14
- package/dist/chunk-EFCQSMXB.js +37 -0
- package/dist/{chunk-2ANOROX3.js → chunk-EZIN5E5B.js} +8 -8
- package/dist/{chunk-BWZ77RQT.js → chunk-F6H2RJ3N.js} +1 -1
- package/dist/{chunk-OLQJTJJU.js → chunk-FAJDKNSD.js} +7 -3
- package/dist/chunk-FGAGWYZB.js +47 -0
- package/dist/{chunk-4WBVUHXN.js → chunk-FLVZFTLQ.js} +18 -18
- package/dist/chunk-FQ6WH6PB.js +140 -0
- package/dist/{chunk-T6XQL73F.js → chunk-FRHD22WM.js} +61 -23
- package/dist/{chunk-XAKWMCXI.js → chunk-FVMPFCDS.js} +4 -4
- package/dist/{chunk-B2WLAWNJ.js → chunk-FY3QIRTL.js} +3 -3
- package/dist/{chunk-CPA6HPVR.js → chunk-FYBF5MB2.js} +15 -11
- package/dist/{chunk-V2YTAIQO.js → chunk-G5YJBNDZ.js} +21 -6
- package/dist/chunk-G6IQYM25.js +339 -0
- package/dist/chunk-G6X4YL4D.js +161 -0
- package/dist/{chunk-SJRGMG6A.js → chunk-GC2VK3JT.js} +25 -25
- package/dist/{chunk-SP2I63OO.js → chunk-GCS3FZOB.js} +77 -3
- package/dist/{chunk-6TACQN3N.js → chunk-GDZB57IU.js} +5 -5
- package/dist/chunk-GIFMBCTW.js +221 -0
- package/dist/{chunk-YY6ZQQBP.js → chunk-GK4PYVEC.js} +18 -8
- package/dist/{chunk-TDD55CJH.js → chunk-GNWAOVFQ.js} +6 -5
- package/dist/chunk-GS76RXQE.js +226 -0
- package/dist/{chunk-YYQN46FY.js → chunk-GSGIZ2OO.js} +23 -23
- package/dist/chunk-GUYL7RMK.js +60 -0
- package/dist/chunk-GX74YWR7.js +321 -0
- package/dist/chunk-HE3HRDNZ.js +122 -0
- package/dist/{chunk-AIOQPLA5.js → chunk-HGHRSF3E.js} +30 -12
- package/dist/{chunk-2RMYG36Q.js → chunk-HK3VOHIE.js} +198 -336
- package/dist/{chunk-QZSX3F7C.js → chunk-HS4BGB4G.js} +24 -22
- package/dist/{chunk-XY3P7JW7.js → chunk-HSZMPH5T.js} +834 -149
- package/dist/{chunk-L6SGQIR3.js → chunk-HWD4JPU7.js} +4 -2
- package/dist/{chunk-PCVDV3UZ.js → chunk-HXXP2NTE.js} +6 -6
- package/dist/{chunk-AA6YULYB.js → chunk-I2A3Z5AK.js} +7 -7
- package/dist/{chunk-N7IXEHK3.js → chunk-ICHDSWEY.js} +7 -7
- package/dist/chunk-IFFRGC3N.js +31 -0
- package/dist/{chunk-4BN53TG3.js → chunk-IFHDV6KQ.js} +31 -23
- package/dist/chunk-J2O4LFVI.js +80 -0
- package/dist/chunk-J5AAZ6OJ.js +161 -0
- package/dist/{chunk-ZMFPW66A.js → chunk-JAABT3DA.js} +3 -3
- package/dist/chunk-JITJP64K.js +34 -0
- package/dist/chunk-JKIGL3XQ.js +162 -0
- package/dist/chunk-JRVNXLZP.js +615 -0
- package/dist/chunk-JRXHON4E.js +41 -0
- package/dist/{chunk-DHPACDG7.js → chunk-JS6WMC3J.js} +8 -8
- package/dist/chunk-JU4QNJSR.js +62 -0
- package/dist/{chunk-PAMPPUR6.js → chunk-JYMIB5XY.js} +1697 -172
- package/dist/{chunk-RRTKKSLQ.js → chunk-JYTIHTIK.js} +8 -6
- package/dist/{chunk-3HI6Z55Y.js → chunk-K332XW7K.js} +324 -39
- package/dist/{chunk-GLUX4EEP.js → chunk-K6JB6XTX.js} +9 -9
- package/dist/chunk-KDAHXQIE.js +294 -0
- package/dist/chunk-KYZMRSPS.js +93 -0
- package/dist/chunk-L4CESDPB.js +22 -0
- package/dist/{chunk-NXFPS2LR.js → chunk-LEBR5NHW.js} +14 -14
- package/dist/chunk-LIJJCBQI.js +223 -0
- package/dist/{chunk-DE76JROX.js → chunk-LIRVU3HW.js} +16 -16
- package/dist/{chunk-JMSCMUCA.js → chunk-LN77DXBM.js} +145 -21
- package/dist/{chunk-JIITQXFB.js → chunk-LNWEWYCM.js} +2969 -788
- package/dist/{chunk-TANFV3MZ.js → chunk-LO3BLMVG.js} +3 -3
- package/dist/chunk-LPV6T362.js +250 -0
- package/dist/{chunk-2VHF7OXM.js → chunk-LQH4P45K.js} +1 -1
- package/dist/chunk-LU4MQVAL.js +41 -0
- package/dist/chunk-LWDNKE4X.js +273 -0
- package/dist/chunk-M4H2UG4R.js +218 -0
- package/dist/{chunk-6BVONWJ7.js → chunk-M6D2U3YM.js} +3 -3
- package/dist/chunk-MD7KUEJ3.js +514 -0
- package/dist/{chunk-66YVAGGM.js → chunk-MMZUN7ZO.js} +20 -20
- package/dist/chunk-MZGRDONP.js +916 -0
- package/dist/chunk-N3RUGTGV.js +39 -0
- package/dist/{chunk-JGZCG5VM.js → chunk-NCB6656Z.js} +7 -7
- package/dist/{chunk-4NDAAFQT.js → chunk-NRQ65LTQ.js} +7 -5
- package/dist/{chunk-UNM6ZOKP.js → chunk-NTAVOWYD.js} +145 -18
- package/dist/{chunk-3USYZGRF.js → chunk-NYOABJNK.js} +36 -27
- package/dist/{chunk-WVPJSRRJ.js → chunk-O4IKAIZ2.js} +24 -20
- package/dist/{chunk-D3PO4DOZ.js → chunk-ODMBPDDV.js} +51 -14
- package/dist/chunk-OKUB6RJA.js +533 -0
- package/dist/chunk-OMPENNWY.js +39 -0
- package/dist/chunk-OPQZISG3.js +25 -0
- package/dist/{chunk-5GGOLAKC.js → chunk-OV57AHCG.js} +26 -26
- package/dist/chunk-OZJ3JLYI.js +58 -0
- package/dist/{chunk-KEM4SDDP.js → chunk-P2VBXX7N.js} +11 -10
- package/dist/{chunk-S2A7YC3Q.js → chunk-PAINMSID.js} +10150 -1720
- package/dist/chunk-PKJAV25S.js +43 -0
- package/dist/{chunk-EBEW3XXQ.js → chunk-PNQLCRSS.js} +4 -4
- package/dist/{chunk-WY6PP6AA.js → chunk-PQH56YF6.js} +37 -5
- package/dist/chunk-PRTBPXYA.js +50 -0
- package/dist/{chunk-YEDDOYSC.js → chunk-PRW3HXM3.js} +2 -2
- package/dist/chunk-PRZ7KMWL.js +42 -0
- package/dist/{chunk-6M5NK3K3.js → chunk-PTQ3ZALZ.js} +42 -4
- package/dist/{chunk-5GDRCDQB.js → chunk-PUEWDYGP.js} +2 -2
- package/dist/chunk-Q26LDIUV.js +236 -0
- package/dist/{chunk-CR3MTH32.js → chunk-Q46QDDMF.js} +77 -7
- package/dist/chunk-QBNWF5ZU.js +19 -0
- package/dist/chunk-QE527XRY.js +342 -0
- package/dist/{chunk-CNUKCS2T.js → chunk-QHJJMZLX.js} +1518 -54
- package/dist/chunk-QJCJOMW3.js +425 -0
- package/dist/{chunk-VYDBJ73M.js → chunk-QKMRFNMA.js} +15 -15
- package/dist/chunk-QOOEYDNJ.js +317 -0
- package/dist/chunk-QSIDVFB7.js +45 -0
- package/dist/{chunk-R77KLRB3.js → chunk-RA33EYS4.js} +19 -18
- package/dist/{chunk-DFBCSHVT.js → chunk-RALY3E5U.js} +4 -4
- package/dist/{chunk-ZCJDOK2S.js → chunk-S2RY3BOD.js} +35 -21
- package/dist/chunk-S5LSDXV4.js +40 -0
- package/dist/chunk-SB5GRUFM.js +54 -0
- package/dist/chunk-SH27FYPF.js +150 -0
- package/dist/chunk-T74WQQGS.js +40 -0
- package/dist/{chunk-O3WI5KWW.js → chunk-TGVVUKTG.js} +2 -2
- package/dist/chunk-THN26IEF.js +48 -0
- package/dist/chunk-TJH2EUAZ.js +47 -0
- package/dist/{chunk-ZNCEKR4J.js → chunk-TQ4TG57V.js} +12 -8
- package/dist/{chunk-7FBGJFEE.js → chunk-TQTRWENN.js} +12 -12
- package/dist/{chunk-TYAID7B7.js → chunk-TSM6T6GQ.js} +23 -23
- package/dist/{chunk-DPZHZQKE.js → chunk-TW3KOSQK.js} +43 -10
- package/dist/{chunk-MC3H7736.js → chunk-TWQRPM76.js} +37 -4
- package/dist/chunk-TYD6I4CZ.js +109 -0
- package/dist/chunk-U3NBK4EU.js +420 -0
- package/dist/{chunk-6JCPI55B.js → chunk-UDXWIPCB.js} +47 -7
- package/dist/{chunk-MMOSZPWJ.js → chunk-UEVZZZU2.js} +4 -4
- package/dist/{chunk-JBTK5OP2.js → chunk-UF4UHCIG.js} +2 -2
- package/dist/{chunk-6JUBZBMW.js → chunk-UFE36WJH.js} +4 -4
- package/dist/{chunk-ONLS5QA5.js → chunk-URIKUID7.js} +12 -12
- package/dist/chunk-UYLBRRFK.js +1106 -0
- package/dist/chunk-UZACEG2C.js +515 -0
- package/dist/{chunk-7SW2QWLU.js → chunk-VAMHOHUU.js} +12 -12
- package/dist/{chunk-RZPYKDWS.js → chunk-VUVGX5L2.js} +1 -1
- package/dist/{chunk-Q4D5X7QB.js → chunk-VYOCPTUB.js} +44 -24
- package/dist/{chunk-CAQZUNOH.js → chunk-WAZEXEJH.js} +567 -3
- package/dist/chunk-WCEF7H6L.js +221 -0
- package/dist/{chunk-2DIQQJCB.js → chunk-WEOMJHPV.js} +12 -12
- package/dist/{chunk-FHH4I4ZV.js → chunk-WFU77T3A.js} +12 -6
- package/dist/chunk-WIWTJMMR.js +292 -0
- package/dist/{chunk-2BFCZ3RK.js → chunk-WNTIOBOG.js} +18 -18
- package/dist/{chunk-7WD63HU2.js → chunk-X2CUXTWU.js} +6 -6
- package/dist/chunk-X7EMBM5X.js +34 -0
- package/dist/chunk-XEQCQO66.js +33 -0
- package/dist/{chunk-F7HYOUFF.js → chunk-XG4WJHLQ.js} +20 -4
- package/dist/{chunk-EFHQ3TWP.js → chunk-XOL3GFW2.js} +171 -31
- package/dist/{chunk-NE2W3OO7.js → chunk-XQ3YZ4QW.js} +22 -22
- package/dist/{chunk-CEXSU4S6.js → chunk-Y6K3CJBU.js} +723 -85
- package/dist/{chunk-4RS5KZQD.js → chunk-YAH5IQAL.js} +5 -5
- package/dist/{chunk-64EQYWCA.js → chunk-YBOVNMHC.js} +51 -14
- package/dist/chunk-YF5DZCPN.js +1212 -0
- package/dist/chunk-YIGBJNH4.js +37 -0
- package/dist/{chunk-BKVWTZ6A.js → chunk-YJ3TBOTK.js} +25 -25
- package/dist/{chunk-Q5AMFEWD.js → chunk-YKTE2RU3.js} +2 -2
- package/dist/{chunk-7EKXJZ2Y.js → chunk-YO2PKTXT.js} +296 -37
- package/dist/{chunk-6J5OKA4Y.js → chunk-YTTNTQOX.js} +15 -15
- package/dist/{chunk-OT2O76OS.js → chunk-YWAKDNMH.js} +183 -33
- package/dist/{chunk-R6XF6UR3.js → chunk-YZDZOEV4.js} +4 -4
- package/dist/chunk-Z3QFH7YG.js +17 -0
- package/dist/{chunk-W5BVQF44.js → chunk-Z6VLC5X4.js} +17 -17
- package/dist/{chunk-HLJAXMM4.js → chunk-ZFQMMU5Y.js} +2 -2
- package/dist/{chunk-W3BQAZ3Q.js → chunk-ZKYRDV7A.js} +12 -12
- package/dist/{chunk-ZMAINWBW.js → chunk-ZLS6PU2O.js} +3 -3
- package/dist/{chunk-ZVM6AKKM.js → chunk-ZOSD4FXV.js} +3 -3
- package/dist/chunk-ZPDDEVQX.js +47 -0
- package/dist/{chunk-TXLH6WHQ.js → chunk-ZQLRCA2V.js} +11 -11
- package/dist/{chunk-HITVBIJS.js → chunk-ZX7235KR.js} +128 -54
- package/dist/{chunk-GT4I4ZWM.js → chunk-ZZFHLRQW.js} +18 -16
- package/dist/files.json +636 -364
- package/dist/packages/naysayer/constitution/constitution.yaml +19 -0
- package/dist/packages/sdk/npm/index.js +5614 -2343
- package/dist/packages/ui/dist/assets/{elk-api-tma0MCuh.js → elk-api-CowTnhEJ.js} +2 -2
- package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +1 -0
- package/dist/packages/ui/dist/assets/index-z2wv0d5d.js +47 -0
- package/dist/packages/ui/dist/index.html +2 -2
- package/package.json +4 -4
- package/dist/chunk-2BF2EYJV.js +0 -174
- package/dist/chunk-42EVSKME.js +0 -37
- package/dist/chunk-4BKGMUIK.js +0 -53
- package/dist/chunk-5KLGBXG5.js +0 -48
- package/dist/chunk-6BNC6EQ4.js +0 -41
- package/dist/chunk-6D2CQ5II.js +0 -41
- package/dist/chunk-7AVHLZGD.js +0 -45
- package/dist/chunk-D46WUV3T.js +0 -33
- package/dist/chunk-E36QOJMD.js +0 -47
- package/dist/chunk-FQXYPF7E.js +0 -147
- package/dist/chunk-GBRFVT63.js +0 -45
- package/dist/chunk-ICY36XCH.js +0 -34
- package/dist/chunk-J4K3KVLD.js +0 -131
- package/dist/chunk-JZU3H6UQ.js +0 -54
- package/dist/chunk-NN2ZQEB4.js +0 -38
- package/dist/chunk-PCOCACY3.js +0 -385
- package/dist/chunk-RTEYOSJY.js +0 -29
- package/dist/chunk-RVH5SUW5.js +0 -69
- package/dist/chunk-VN3DP4LQ.js +0 -43
- package/dist/chunk-XPTOBS2D.js +0 -48
- package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +0 -29
- package/dist/packages/ui/dist/assets/index-DD_hufQz.css +0 -1
|
@@ -3,52 +3,48 @@ import { fileURLToPath as __atticusFileURLToPath } from "node:url";
|
|
|
3
3
|
var require = __atticusCreateRequire(import.meta.url);
|
|
4
4
|
var __atticusUrl = (p) => new URL("./" + p, import.meta.url).href;
|
|
5
5
|
var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
|
|
6
|
-
import {
|
|
7
|
-
listRuns,
|
|
8
|
-
runTranscript
|
|
9
|
-
} from "./chunk-DPUYDDQG.js";
|
|
10
6
|
import {
|
|
11
7
|
historyEntry,
|
|
12
8
|
listHistory
|
|
13
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3WON4G3M.js";
|
|
10
|
+
import "./chunk-JYTIHTIK.js";
|
|
14
11
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./chunk-
|
|
12
|
+
uiComponentTypeRoutes
|
|
13
|
+
} from "./chunk-CG6CULP6.js";
|
|
17
14
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
answerAudit,
|
|
16
|
+
auditLines,
|
|
17
|
+
defaultAuditSeed,
|
|
18
|
+
precisionOf,
|
|
19
|
+
prepareAudit,
|
|
20
|
+
writeAudit
|
|
21
|
+
} from "./chunk-7FUIVIBG.js";
|
|
22
|
+
import {
|
|
23
|
+
listRuns,
|
|
24
|
+
partStates,
|
|
25
|
+
runTranscript
|
|
26
|
+
} from "./chunk-BA2HUMDN.js";
|
|
23
27
|
import {
|
|
24
|
-
HttpError,
|
|
25
|
-
badRequest,
|
|
26
|
-
conflict,
|
|
27
|
-
notFound,
|
|
28
|
-
startServer,
|
|
29
|
-
strParam,
|
|
30
28
|
uiCiteRoutes
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-RRTKKSLQ.js";
|
|
29
|
+
} from "./chunk-WCEF7H6L.js";
|
|
33
30
|
import {
|
|
34
31
|
isHandoffStep
|
|
35
32
|
} from "./chunk-4Q3NN7VF.js";
|
|
36
33
|
import {
|
|
37
|
-
|
|
38
|
-
} from "./chunk-
|
|
39
|
-
import {
|
|
40
|
-
outcomeList,
|
|
41
|
-
outcomeRecord,
|
|
42
|
-
releaseEvidenceCommand,
|
|
43
|
-
releaseForecastCommand,
|
|
44
|
-
releaseNotesCommand,
|
|
45
|
-
releasePlanCommand,
|
|
46
|
-
releaseSecurityCommand
|
|
47
|
-
} from "./chunk-652QWTLZ.js";
|
|
34
|
+
estimateOf
|
|
35
|
+
} from "./chunk-WFU77T3A.js";
|
|
48
36
|
import {
|
|
49
37
|
changeMapBody,
|
|
50
|
-
changeMapOf
|
|
51
|
-
|
|
38
|
+
changeMapOf,
|
|
39
|
+
targetFlowsOf
|
|
40
|
+
} from "./chunk-GNWAOVFQ.js";
|
|
41
|
+
import {
|
|
42
|
+
changeArchive,
|
|
43
|
+
changeHandoff,
|
|
44
|
+
changeNew,
|
|
45
|
+
changeReleasePrs,
|
|
46
|
+
changeSync
|
|
47
|
+
} from "./chunk-COYN3UAN.js";
|
|
52
48
|
import {
|
|
53
49
|
isSandbox,
|
|
54
50
|
learnCheck,
|
|
@@ -56,141 +52,253 @@ import {
|
|
|
56
52
|
lessonsFor,
|
|
57
53
|
readProgress,
|
|
58
54
|
sandboxCreate
|
|
59
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-ZQLRCA2V.js";
|
|
60
56
|
import {
|
|
61
57
|
defencesScan,
|
|
62
58
|
evalRun
|
|
63
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-FVMPFCDS.js";
|
|
64
60
|
import {
|
|
65
61
|
listReports,
|
|
66
62
|
readQuality
|
|
67
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-FYBF5MB2.js";
|
|
68
64
|
import {
|
|
69
|
-
|
|
70
|
-
} from "./chunk-
|
|
65
|
+
conformanceCommand
|
|
66
|
+
} from "./chunk-G5YJBNDZ.js";
|
|
71
67
|
import {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
outcomeList,
|
|
69
|
+
outcomeRecord,
|
|
70
|
+
releaseEvidenceCommand,
|
|
71
|
+
releaseForecastCommand,
|
|
72
|
+
releaseNotesCommand,
|
|
73
|
+
releasePlanCommand,
|
|
74
|
+
releaseSecurityCommand
|
|
75
|
+
} from "./chunk-AZDITBCT.js";
|
|
76
|
+
import {
|
|
77
|
+
readServiceRequests
|
|
78
|
+
} from "./chunk-36RY7ZXM.js";
|
|
75
79
|
import {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
runDone,
|
|
81
|
+
taskRun,
|
|
82
|
+
taskRunNotWaiting
|
|
83
|
+
} from "./chunk-AOCWFA7T.js";
|
|
80
84
|
import {
|
|
81
85
|
executorsCheck,
|
|
82
86
|
todoDone,
|
|
83
87
|
todoRun,
|
|
84
88
|
todoShow
|
|
85
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-ZFQMMU5Y.js";
|
|
86
90
|
import {
|
|
87
91
|
inputsSha256,
|
|
88
92
|
loadExecutors
|
|
89
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-DDJPM5YX.js";
|
|
90
94
|
import {
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
initiativeStatus,
|
|
96
|
+
readView
|
|
97
|
+
} from "./chunk-2V2ZPQQU.js";
|
|
93
98
|
import {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
DecideError,
|
|
100
|
+
correctDecision,
|
|
101
|
+
decisionsView,
|
|
102
|
+
undoBatch
|
|
103
|
+
} from "./chunk-UYLBRRFK.js";
|
|
97
104
|
import {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
boxesOf,
|
|
106
|
+
candidatesOf,
|
|
107
|
+
uiLinkRoutes
|
|
108
|
+
} from "./chunk-6DQYZU2W.js";
|
|
101
109
|
import {
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
matchDone,
|
|
111
|
+
quotesOf,
|
|
112
|
+
readMatch,
|
|
113
|
+
runMatch
|
|
114
|
+
} from "./chunk-QJCJOMW3.js";
|
|
115
|
+
import "./chunk-3SSTNB4E.js";
|
|
116
|
+
import "./chunk-JRVNXLZP.js";
|
|
117
|
+
import {
|
|
118
|
+
copilotAcpAllowed
|
|
119
|
+
} from "./chunk-LIJJCBQI.js";
|
|
120
|
+
import "./chunk-26Z7OGQW.js";
|
|
121
|
+
import "./chunk-OKUB6RJA.js";
|
|
122
|
+
import {
|
|
123
|
+
filesUnder,
|
|
124
|
+
memoByFiles,
|
|
125
|
+
recheckNow,
|
|
126
|
+
uiFactsRoutes
|
|
127
|
+
} from "./chunk-QOOEYDNJ.js";
|
|
128
|
+
import {
|
|
129
|
+
HttpError,
|
|
130
|
+
badRequest,
|
|
131
|
+
conflict,
|
|
132
|
+
notFound,
|
|
133
|
+
startServer,
|
|
134
|
+
strParam
|
|
135
|
+
} from "./chunk-HK3VOHIE.js";
|
|
136
|
+
import {
|
|
137
|
+
safeMessage,
|
|
138
|
+
withFileLock
|
|
139
|
+
} from "./chunk-THN26IEF.js";
|
|
140
|
+
import "./chunk-LN77DXBM.js";
|
|
141
|
+
import "./chunk-L4CESDPB.js";
|
|
142
|
+
import "./chunk-U3NBK4EU.js";
|
|
143
|
+
import "./chunk-455SYIIE.js";
|
|
104
144
|
import {
|
|
105
145
|
reconcileCommand
|
|
106
|
-
} from "./chunk-
|
|
146
|
+
} from "./chunk-YZDZOEV4.js";
|
|
107
147
|
import {
|
|
108
148
|
sourcesAdd,
|
|
109
149
|
sourcesSync
|
|
110
|
-
} from "./chunk-
|
|
150
|
+
} from "./chunk-HXXP2NTE.js";
|
|
111
151
|
import {
|
|
112
152
|
INPUTS_SOURCE_ID
|
|
113
|
-
} from "./chunk-
|
|
153
|
+
} from "./chunk-D57MFJPP.js";
|
|
154
|
+
import "./chunk-5QUOPNLN.js";
|
|
155
|
+
import {
|
|
156
|
+
readRepos
|
|
157
|
+
} from "./chunk-4TKVU2I2.js";
|
|
158
|
+
import {
|
|
159
|
+
uiOnboardingRoutes
|
|
160
|
+
} from "./chunk-PNQLCRSS.js";
|
|
161
|
+
import "./chunk-3J5I7ZOU.js";
|
|
162
|
+
import {
|
|
163
|
+
uiTrustRoutes
|
|
164
|
+
} from "./chunk-4FOAYLI4.js";
|
|
165
|
+
import {
|
|
166
|
+
contractCheckOf,
|
|
167
|
+
estateJoinOnFile,
|
|
168
|
+
stampOf
|
|
169
|
+
} from "./chunk-GX74YWR7.js";
|
|
170
|
+
import "./chunk-G6IQYM25.js";
|
|
171
|
+
import "./chunk-LPV6T362.js";
|
|
172
|
+
import {
|
|
173
|
+
digestCommand
|
|
174
|
+
} from "./chunk-X2CUXTWU.js";
|
|
114
175
|
import {
|
|
115
176
|
baselineCommand,
|
|
116
177
|
setupSteps
|
|
117
|
-
} from "./chunk-
|
|
118
|
-
import {
|
|
119
|
-
catalogRecords,
|
|
120
|
-
openChanges,
|
|
121
|
-
systemGuide
|
|
122
|
-
} from "./chunk-B2WLAWNJ.js";
|
|
178
|
+
} from "./chunk-GK4PYVEC.js";
|
|
123
179
|
import {
|
|
124
180
|
doctorChecks
|
|
125
|
-
} from "./chunk-
|
|
126
|
-
import "./chunk-
|
|
127
|
-
import "./chunk-YISXLPHN.js";
|
|
128
|
-
import "./chunk-GDLULHTC.js";
|
|
181
|
+
} from "./chunk-HGHRSF3E.js";
|
|
182
|
+
import "./chunk-4B6BT75O.js";
|
|
129
183
|
import {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
} from "./chunk-4BN53TG3.js";
|
|
134
|
-
import "./chunk-2VHF7OXM.js";
|
|
135
|
-
import "./chunk-KEM4SDDP.js";
|
|
184
|
+
readMe,
|
|
185
|
+
writeMe
|
|
186
|
+
} from "./chunk-UF4UHCIG.js";
|
|
136
187
|
import {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
188
|
+
cancelTaskRunWait,
|
|
189
|
+
findTaskRunOfRun,
|
|
190
|
+
previewTaskRun,
|
|
191
|
+
raiseTaskPr,
|
|
192
|
+
readTaskRun,
|
|
193
|
+
readyTasksOf,
|
|
194
|
+
runReadyTasks,
|
|
195
|
+
saveTaskRun,
|
|
196
|
+
settleTaskPr,
|
|
197
|
+
stopAllBuilds,
|
|
198
|
+
taskRunOfRun
|
|
199
|
+
} from "./chunk-Y6K3CJBU.js";
|
|
200
|
+
import "./chunk-GIFMBCTW.js";
|
|
201
|
+
import "./chunk-MD7KUEJ3.js";
|
|
142
202
|
import {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
203
|
+
checkoutOf
|
|
204
|
+
} from "./chunk-Q46QDDMF.js";
|
|
205
|
+
import "./chunk-TGVVUKTG.js";
|
|
206
|
+
import {
|
|
207
|
+
intakeContext
|
|
208
|
+
} from "./chunk-TQ4TG57V.js";
|
|
209
|
+
import {
|
|
210
|
+
workspaceOpen,
|
|
211
|
+
workspaceSync
|
|
212
|
+
} from "./chunk-IFHDV6KQ.js";
|
|
213
|
+
import "./chunk-LQH4P45K.js";
|
|
214
|
+
import "./chunk-P2VBXX7N.js";
|
|
148
215
|
import {
|
|
149
216
|
archiveGate,
|
|
150
217
|
ciArchiveGate
|
|
151
|
-
} from "./chunk-
|
|
218
|
+
} from "./chunk-ZX7235KR.js";
|
|
152
219
|
import {
|
|
153
220
|
traceMatrix
|
|
154
|
-
} from "./chunk-
|
|
155
|
-
import "./chunk-
|
|
156
|
-
import "./chunk-VOGCSYOI.js";
|
|
221
|
+
} from "./chunk-EZIN5E5B.js";
|
|
222
|
+
import "./chunk-3LZ6P5EP.js";
|
|
157
223
|
import {
|
|
158
224
|
scanCommand
|
|
159
|
-
} from "./chunk-
|
|
160
|
-
import "./chunk-
|
|
225
|
+
} from "./chunk-NCB6656Z.js";
|
|
226
|
+
import "./chunk-TYD6I4CZ.js";
|
|
161
227
|
import {
|
|
162
228
|
readChangeTasks,
|
|
163
|
-
readStory
|
|
164
|
-
|
|
229
|
+
readStory,
|
|
230
|
+
setTaskDone
|
|
231
|
+
} from "./chunk-YWAKDNMH.js";
|
|
232
|
+
import {
|
|
233
|
+
catalogRecords,
|
|
234
|
+
openChanges,
|
|
235
|
+
systemGuide
|
|
236
|
+
} from "./chunk-FY3QIRTL.js";
|
|
237
|
+
import "./chunk-3G4YI4ZU.js";
|
|
238
|
+
import "./chunk-2HTEMYTB.js";
|
|
165
239
|
import {
|
|
166
240
|
ASSISTANT_FORMATS,
|
|
167
241
|
readProbes,
|
|
168
242
|
whatRunsWhere
|
|
169
|
-
} from "./chunk-
|
|
243
|
+
} from "./chunk-3AQ2R5NL.js";
|
|
244
|
+
import {
|
|
245
|
+
critic,
|
|
246
|
+
criticStats,
|
|
247
|
+
findingAction
|
|
248
|
+
} from "./chunk-PTQ3ZALZ.js";
|
|
249
|
+
import "./chunk-A2G72UI4.js";
|
|
250
|
+
import {
|
|
251
|
+
planStories,
|
|
252
|
+
snowDraft,
|
|
253
|
+
snowSubmit
|
|
254
|
+
} from "./chunk-I2A3Z5AK.js";
|
|
255
|
+
import "./chunk-F6H2RJ3N.js";
|
|
170
256
|
import {
|
|
257
|
+
CORRECTIONS_FILE,
|
|
258
|
+
FEATURE_FILE,
|
|
171
259
|
NEEDS_FILE,
|
|
172
260
|
PlannerError,
|
|
173
261
|
SYNC_FILE,
|
|
262
|
+
SnowFillError,
|
|
263
|
+
Stroke,
|
|
174
264
|
TARGET_DIR,
|
|
265
|
+
addCorrection,
|
|
266
|
+
applyStroke,
|
|
175
267
|
codeFactRecords,
|
|
176
268
|
commentOnFinding,
|
|
177
269
|
conflictTouches,
|
|
270
|
+
draftItems,
|
|
271
|
+
draftProvenance,
|
|
272
|
+
drawnStrokes,
|
|
273
|
+
fillSnowFields,
|
|
274
|
+
loadEstate,
|
|
178
275
|
markNeed,
|
|
179
276
|
mergeDelta,
|
|
180
277
|
plainWhy,
|
|
278
|
+
planSketchWrite,
|
|
279
|
+
readCorrections,
|
|
181
280
|
readDeltas,
|
|
182
281
|
readKnowledgeApplies,
|
|
183
282
|
readNeeds,
|
|
184
283
|
readStageLoop,
|
|
284
|
+
recordPersonSave,
|
|
285
|
+
removeCorrection,
|
|
286
|
+
renderFeature,
|
|
185
287
|
setKnowledgeStatus,
|
|
288
|
+
sketchFingerprint,
|
|
186
289
|
stageComments,
|
|
187
|
-
stageGate
|
|
188
|
-
|
|
290
|
+
stageGate,
|
|
291
|
+
writeSketch
|
|
292
|
+
} from "./chunk-BEPCKDCP.js";
|
|
189
293
|
import {
|
|
294
|
+
FeaturePlacement,
|
|
295
|
+
STAGES,
|
|
190
296
|
STAGE_PHASE,
|
|
191
297
|
fieldNames,
|
|
192
298
|
rerunOf
|
|
193
|
-
} from "./chunk-
|
|
299
|
+
} from "./chunk-HWD4JPU7.js";
|
|
300
|
+
import "./chunk-5WERBKLE.js";
|
|
301
|
+
import "./chunk-VOGCSYOI.js";
|
|
194
302
|
import {
|
|
195
303
|
layerRules,
|
|
196
304
|
loadConstitution,
|
|
@@ -203,8 +311,9 @@ import {
|
|
|
203
311
|
readStoryIndex,
|
|
204
312
|
reviewedGates,
|
|
205
313
|
roundsOf
|
|
206
|
-
} from "./chunk-
|
|
314
|
+
} from "./chunk-K332XW7K.js";
|
|
207
315
|
import {
|
|
316
|
+
ChangeOwnersError,
|
|
208
317
|
artifactFiles,
|
|
209
318
|
artifactPhase,
|
|
210
319
|
changeFolder,
|
|
@@ -214,19 +323,27 @@ import {
|
|
|
214
323
|
detectConflicts,
|
|
215
324
|
liveHandoff,
|
|
216
325
|
loadChangeContext,
|
|
217
|
-
requiredArtifacts
|
|
218
|
-
|
|
219
|
-
|
|
326
|
+
requiredArtifacts,
|
|
327
|
+
setChangeOwners
|
|
328
|
+
} from "./chunk-HSZMPH5T.js";
|
|
329
|
+
import "./chunk-D2WSW4GU.js";
|
|
220
330
|
import {
|
|
221
331
|
fileStepStore,
|
|
222
332
|
manualAdapter,
|
|
223
333
|
stepOfProvisional
|
|
224
|
-
} from "./chunk-
|
|
334
|
+
} from "./chunk-4KNOJC6J.js";
|
|
225
335
|
import {
|
|
336
|
+
componentRenames,
|
|
337
|
+
estateFacts,
|
|
338
|
+
estateRepos,
|
|
226
339
|
forgetCatalogs,
|
|
340
|
+
hostAliases,
|
|
341
|
+
joinReposOf,
|
|
227
342
|
keepCatalogsInMemory,
|
|
228
|
-
|
|
229
|
-
|
|
343
|
+
outsideProfileSystems,
|
|
344
|
+
readCatalog,
|
|
345
|
+
targetPicker
|
|
346
|
+
} from "./chunk-4SLBCEPD.js";
|
|
230
347
|
import {
|
|
231
348
|
OrgPackProposal,
|
|
232
349
|
SNAPSHOT_FILE,
|
|
@@ -250,106 +367,128 @@ import {
|
|
|
250
367
|
snapshotDates,
|
|
251
368
|
snapshotSources,
|
|
252
369
|
writeReview
|
|
253
|
-
} from "./chunk-
|
|
370
|
+
} from "./chunk-64AADMTW.js";
|
|
254
371
|
import {
|
|
255
372
|
NoSuchNode,
|
|
256
|
-
applyCorrections,
|
|
257
373
|
buildEstate,
|
|
258
|
-
correctedFact,
|
|
259
|
-
correctionFor,
|
|
260
374
|
focusView,
|
|
261
|
-
frameworkFacts,
|
|
262
|
-
frameworkGaps,
|
|
263
375
|
nodeView,
|
|
264
376
|
viewKey
|
|
265
|
-
} from "./chunk-
|
|
377
|
+
} from "./chunk-JYMIB5XY.js";
|
|
266
378
|
import {
|
|
267
379
|
readServiceRequestExports,
|
|
380
|
+
readVendorSpec,
|
|
268
381
|
readWorkItemExports
|
|
269
|
-
} from "./chunk-
|
|
270
|
-
import "./chunk-
|
|
271
|
-
import "./chunk-
|
|
272
|
-
import "./chunk-
|
|
273
|
-
import "./chunk-
|
|
382
|
+
} from "./chunk-CPK7QSBG.js";
|
|
383
|
+
import "./chunk-YO2PKTXT.js";
|
|
384
|
+
import "./chunk-TWQRPM76.js";
|
|
385
|
+
import "./chunk-VUVGX5L2.js";
|
|
386
|
+
import "./chunk-API5TOUB.js";
|
|
274
387
|
import {
|
|
275
388
|
baseClonePath,
|
|
276
389
|
loadRepos,
|
|
277
390
|
readChangeRepos
|
|
278
|
-
} from "./chunk-
|
|
279
|
-
import "./chunk-GQ4N4WKF.js";
|
|
391
|
+
} from "./chunk-XOL3GFW2.js";
|
|
280
392
|
import {
|
|
281
393
|
COMMON_SETTINGS,
|
|
282
394
|
PROVIDER_SETTINGS,
|
|
283
395
|
defaultKeyStore,
|
|
284
396
|
looksLikeKey
|
|
285
|
-
} from "./chunk-
|
|
397
|
+
} from "./chunk-PRW3HXM3.js";
|
|
286
398
|
import {
|
|
287
399
|
providerFactoryFor
|
|
288
|
-
} from "./chunk-
|
|
400
|
+
} from "./chunk-QHJJMZLX.js";
|
|
401
|
+
import "./chunk-7AB6OWTV.js";
|
|
289
402
|
import {
|
|
290
403
|
ASSISTANTS,
|
|
291
404
|
ChangeArtifact,
|
|
292
405
|
DEFAULT_PORT,
|
|
293
406
|
DEFAULT_RUNTIME,
|
|
407
|
+
DeclareError,
|
|
294
408
|
DiagramLayout,
|
|
295
|
-
FACT_FIXED_FIELDS,
|
|
296
|
-
FactCorrection,
|
|
297
409
|
KNOWLEDGE_KINDS,
|
|
298
410
|
KNOWLEDGE_RECORD_KIND,
|
|
299
411
|
LAYOUT_SCHEMA_VERSION,
|
|
300
412
|
MANUAL_SYSTEMS,
|
|
413
|
+
ManifestEditError,
|
|
301
414
|
QuestionError,
|
|
415
|
+
QuestionRole,
|
|
302
416
|
QuestionState,
|
|
303
417
|
RUN_SCHEMA_VERSION,
|
|
304
418
|
RecordId,
|
|
305
419
|
Runtime,
|
|
306
420
|
SOURCE_KIND_SUGGESTIONS,
|
|
421
|
+
SPEC_HEADER_FILE,
|
|
422
|
+
TARGETS_STEP_ID,
|
|
423
|
+
TOPICS_STEP_ID,
|
|
307
424
|
ViewKey,
|
|
425
|
+
acceptProposal,
|
|
426
|
+
addProfileSystem,
|
|
427
|
+
addRolesTeam,
|
|
428
|
+
answerByWords,
|
|
308
429
|
answerQuestion,
|
|
309
430
|
api_exports,
|
|
310
431
|
catalogDirOf,
|
|
432
|
+
changedLines,
|
|
311
433
|
changesDirOf,
|
|
312
434
|
changesRelOf,
|
|
313
|
-
|
|
435
|
+
copilotAcpOf,
|
|
436
|
+
currentSignal,
|
|
437
|
+
decideOf,
|
|
438
|
+
declareLink,
|
|
439
|
+
declaredOf,
|
|
440
|
+
diagramsRelOf,
|
|
441
|
+
eolOf,
|
|
314
442
|
external_exports,
|
|
443
|
+
factIdPart,
|
|
315
444
|
factsDirOf,
|
|
316
|
-
factsFileOf,
|
|
317
445
|
factsRelOf,
|
|
318
446
|
gateRoundLimit,
|
|
447
|
+
inEol,
|
|
319
448
|
inputsDirOf,
|
|
320
449
|
isAdopted,
|
|
450
|
+
joinConventions,
|
|
451
|
+
joinSharedMaps,
|
|
452
|
+
joinVendorOperations,
|
|
321
453
|
knowledgeDirOf,
|
|
322
454
|
knowledgeKindOf,
|
|
323
455
|
knowledgeRelOf,
|
|
324
456
|
layoutPath,
|
|
457
|
+
lfOnly,
|
|
325
458
|
listQuestions,
|
|
326
459
|
loadOrgPack,
|
|
460
|
+
manifestFiles,
|
|
327
461
|
orgPackDirOf,
|
|
328
462
|
pageStanding,
|
|
329
|
-
|
|
330
|
-
parseFactsLines,
|
|
463
|
+
parseCorrections,
|
|
331
464
|
proposalsDirOf,
|
|
332
465
|
proposalsRelOf,
|
|
333
466
|
readEstateJoins,
|
|
334
467
|
readQuestion,
|
|
335
468
|
readRepoSpec,
|
|
469
|
+
readSharedMaps,
|
|
336
470
|
require_dist,
|
|
337
471
|
runsDirOf,
|
|
338
472
|
runsRelOf,
|
|
473
|
+
setManifestField,
|
|
474
|
+
specDirOf,
|
|
339
475
|
specRelOf,
|
|
340
|
-
specRepos
|
|
341
|
-
|
|
476
|
+
specRepos,
|
|
477
|
+
undeclareLink
|
|
478
|
+
} from "./chunk-PAINMSID.js";
|
|
342
479
|
import {
|
|
480
|
+
FileChangedError,
|
|
343
481
|
createLog,
|
|
344
|
-
writeFileAtomic
|
|
345
|
-
|
|
482
|
+
writeFileAtomic,
|
|
483
|
+
writeFileIfUnchanged
|
|
484
|
+
} from "./chunk-ODMBPDDV.js";
|
|
346
485
|
import {
|
|
347
486
|
__toESM
|
|
348
487
|
} from "./chunk-TV45EUGM.js";
|
|
349
488
|
|
|
350
489
|
// packages/cli/dist/ui-commands.js
|
|
351
490
|
import { spawn } from "node:child_process";
|
|
352
|
-
import { isAbsolute, join as
|
|
491
|
+
import { isAbsolute, join as join14, relative as relative2, sep as sep2 } from "node:path";
|
|
353
492
|
|
|
354
493
|
// packages/ui/dist-lib/index.js
|
|
355
494
|
import { dirname, join } from "node:path";
|
|
@@ -361,8 +500,45 @@ function uiDistDir() {
|
|
|
361
500
|
|
|
362
501
|
// packages/cli/dist/ui-api.js
|
|
363
502
|
import { createHash } from "node:crypto";
|
|
364
|
-
import {
|
|
365
|
-
import {
|
|
503
|
+
import { existsSync } from "node:fs";
|
|
504
|
+
import { mkdir, readdir, readFile, stat } from "node:fs/promises";
|
|
505
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
506
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
507
|
+
var FACTS_REPO = /^[a-z0-9][a-z0-9._-]{0,99}$/i;
|
|
508
|
+
async function openTaskWorktree(root, change, task, gitHost) {
|
|
509
|
+
const p = await previewTaskRun(root, change, task, {
|
|
510
|
+
skipGitHost: true,
|
|
511
|
+
...gitHost ? { gitHost } : {}
|
|
512
|
+
}).catch(() => void 0);
|
|
513
|
+
if (!p)
|
|
514
|
+
return void 0;
|
|
515
|
+
const text = await readFile(join2(changesDirOf(root), change, "repos.yaml"), "utf8").catch(() => "");
|
|
516
|
+
let has = false;
|
|
517
|
+
try {
|
|
518
|
+
const d = (0, import_yaml.parse)(text);
|
|
519
|
+
has = !!d?.repos?.some((r) => r.id === p.repo && r.worktree);
|
|
520
|
+
} catch {
|
|
521
|
+
}
|
|
522
|
+
if (has)
|
|
523
|
+
return void 0;
|
|
524
|
+
if (!existsSync(baseClonePath(root, p.repo))) {
|
|
525
|
+
const synced = await run((io) => workspaceSync(root, [p.repo], io));
|
|
526
|
+
if (synced.code !== 0)
|
|
527
|
+
return synced;
|
|
528
|
+
const opened = await run((io) => workspaceOpen(root, change, [p.repo], io));
|
|
529
|
+
return { ...opened, out: [...synced.out, ...opened.out] };
|
|
530
|
+
}
|
|
531
|
+
return run((io) => workspaceOpen(root, change, [p.repo], io));
|
|
532
|
+
}
|
|
533
|
+
var TASK_ERRORS = /* @__PURE__ */ new Set([
|
|
534
|
+
"SpecKitError",
|
|
535
|
+
"ChangeError",
|
|
536
|
+
"ChangeFileError",
|
|
537
|
+
"AdapterError",
|
|
538
|
+
"RuntimeConfigError",
|
|
539
|
+
"GitError",
|
|
540
|
+
"RuntimeError"
|
|
541
|
+
]);
|
|
366
542
|
async function run(f) {
|
|
367
543
|
const out = [];
|
|
368
544
|
const err = [];
|
|
@@ -413,6 +589,7 @@ async function changeDetail(root, id) {
|
|
|
413
589
|
const current = report.phase ? phases.indexOf(report.phase) : phases.length;
|
|
414
590
|
const artifacts = [];
|
|
415
591
|
const blockedArtifacts = new Set(report.blockers.map((b) => b.artifact).filter(Boolean));
|
|
592
|
+
const templateOnly = new Set(report.blockers.filter((b) => b.code === "unfilled-artifact").map((b) => b.artifact));
|
|
416
593
|
const cctx = await loadChangeContext(root);
|
|
417
594
|
const onStages = (await Promise.all(cctx.policy.stages.map((s) => readFile(join2(dir, ...s.loop.split("/"))).then(() => true, () => false)))).some(Boolean);
|
|
418
595
|
const type = report.type.artifacts;
|
|
@@ -432,6 +609,8 @@ async function changeDetail(root, id) {
|
|
|
432
609
|
artifact: a,
|
|
433
610
|
path: `${changesRelOf(root)}/${id}/${files[0] ?? `${a}.md`}`,
|
|
434
611
|
status: done ? "done" : at <= current ? "ready" : "blocked",
|
|
612
|
+
// Seventh UX walk (2.70.0): ready or blocked says what the phase allows, not that it is there.
|
|
613
|
+
written: files.length > 0 && !templateOnly.has(a),
|
|
435
614
|
openFindings: findings.filter((f) => f.state === "open" && f.artifact.startsWith(a)).length
|
|
436
615
|
});
|
|
437
616
|
}
|
|
@@ -446,6 +625,8 @@ async function changeDetail(root, id) {
|
|
|
446
625
|
openFindings: open,
|
|
447
626
|
updatedAt: c.history.at(-1)?.at ?? c.createdAt,
|
|
448
627
|
...fast ? { fastPath: true } : {},
|
|
628
|
+
// Ninth UX walk (2.83.0): who owns each role, so the change page can name them.
|
|
629
|
+
owners: c.owners,
|
|
449
630
|
artifacts,
|
|
450
631
|
...h ? {
|
|
451
632
|
handoff: {
|
|
@@ -456,13 +637,82 @@ async function changeDetail(root, id) {
|
|
|
456
637
|
} : {}
|
|
457
638
|
};
|
|
458
639
|
}
|
|
640
|
+
async function applyFirstOf(root, files) {
|
|
641
|
+
const mtime = (p) => stat(p).then((s) => s.mtimeMs, () => void 0);
|
|
642
|
+
const out = [];
|
|
643
|
+
for (const f of files) {
|
|
644
|
+
const repo = /(?:^|\/)facts\/([^/]+)\.corrections\.jsonl$/.exec(f.path)?.[1] ?? await answeredMapQuestionRepo(root, f.path);
|
|
645
|
+
if (!repo || out.includes(repo))
|
|
646
|
+
continue;
|
|
647
|
+
const saved = await mtime(join2(root, ...f.path.split("/")));
|
|
648
|
+
if (saved === void 0)
|
|
649
|
+
continue;
|
|
650
|
+
const applied = await mtime(join2(specDirOf(root, repo), SPEC_HEADER_FILE)) ?? await mtime(join2(factsDirOf(root), `${repo}.verify.yaml`));
|
|
651
|
+
if (applied === void 0 || saved > applied)
|
|
652
|
+
out.push(repo);
|
|
653
|
+
}
|
|
654
|
+
return out;
|
|
655
|
+
}
|
|
656
|
+
async function answeredMapQuestionRepo(root, path) {
|
|
657
|
+
if (!/(?:^|\/)questions\/q-[^/]+\.yaml$/.test(path))
|
|
658
|
+
return void 0;
|
|
659
|
+
const q = await readFile(join2(root, ...path.split("/")), "utf8").then((t) => (0, import_yaml.parse)(t), () => void 0);
|
|
660
|
+
if (!q || q.state !== "answered" || typeof q.repo !== "string")
|
|
661
|
+
return void 0;
|
|
662
|
+
const of = /^atticus verify ([a-z0-9][a-z0-9._-]*) --write$/.exec(String(q.rerun ?? "").trim());
|
|
663
|
+
return of?.[1] === q.repo ? q.repo : void 0;
|
|
664
|
+
}
|
|
665
|
+
async function taskViews(root, change, prs) {
|
|
666
|
+
const dir = join2(changesDirOf(root), change);
|
|
667
|
+
let tasks = readChangeTasks(await readChangeFiles(dir));
|
|
668
|
+
const states = prs ? await Promise.all(tasks.map((t) => settleTaskPr(root, change, t.id, {
|
|
669
|
+
...prs.gitHost ? { gitHost: prs.gitHost } : {},
|
|
670
|
+
...prs.now ? { now: () => new Date(prs.now) } : {}
|
|
671
|
+
}).catch(() => ({})))) : [];
|
|
672
|
+
if (states.some((s) => s.ticked)) {
|
|
673
|
+
tasks = readChangeTasks(await readChangeFiles(dir));
|
|
674
|
+
prs?.onTick?.();
|
|
675
|
+
}
|
|
676
|
+
const waits = new Map((await readyTasksOf(root, change).catch(() => ({ tasks: [] }))).tasks.map((x) => [x.id, x]));
|
|
677
|
+
const out = [];
|
|
678
|
+
for (const [i, t] of tasks.entries()) {
|
|
679
|
+
const w = t.done ? void 0 : waits.get(t.id);
|
|
680
|
+
const tr = await readTaskRun(root, change, t.id).catch(() => void 0);
|
|
681
|
+
const run4 = tr ? tr.waiting ?? tr.runs[0] : void 0;
|
|
682
|
+
const prState = tr?.state === "pr-open" ? states[i]?.state : void 0;
|
|
683
|
+
out.push({
|
|
684
|
+
id: t.id,
|
|
685
|
+
repo: t.repo,
|
|
686
|
+
description: t.title,
|
|
687
|
+
done: t.done,
|
|
688
|
+
...t.stories[0] ? { story: t.stories[0] } : {},
|
|
689
|
+
branch: `task/${change}/${t.id}`,
|
|
690
|
+
...run4 ? { run: run4 } : {},
|
|
691
|
+
...tr ? {
|
|
692
|
+
taskRun: {
|
|
693
|
+
state: tr.state,
|
|
694
|
+
level: tr.level,
|
|
695
|
+
branch: tr.branch,
|
|
696
|
+
...tr.commit ? { commit: tr.commit } : {},
|
|
697
|
+
...tr.pr ? { pr: { ...tr.pr, ...prState ? { state: prState } : {} } } : {},
|
|
698
|
+
...tr.ticked ? { ticked: tr.ticked } : {},
|
|
699
|
+
...tr.marked ? { marked: tr.marked } : {},
|
|
700
|
+
notes: tr.notes
|
|
701
|
+
}
|
|
702
|
+
} : {},
|
|
703
|
+
...w?.waitsFor.length ? { waits: { tasks: w.waitsFor, sameRepo: w.sameRepo ?? [] } } : {}
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
return out;
|
|
707
|
+
}
|
|
459
708
|
async function runView(root, change, runId) {
|
|
460
709
|
const dir = join2(runsDirOf(root), change, runId);
|
|
461
|
-
const
|
|
462
|
-
if (!
|
|
710
|
+
const text = await readFile(join2(dir, "run.json"), "utf8").catch(() => void 0);
|
|
711
|
+
if (!text)
|
|
463
712
|
return void 0;
|
|
464
|
-
const r = JSON.parse(
|
|
713
|
+
const r = JSON.parse(text);
|
|
465
714
|
const manifest = await readFile(join2(dir, "context-manifest.json"), "utf8").catch(() => "");
|
|
715
|
+
const tr = await taskRunOfRun(root, change, runId).catch(() => void 0);
|
|
466
716
|
return {
|
|
467
717
|
schemaVersion: RUN_SCHEMA_VERSION,
|
|
468
718
|
runId: r.runId,
|
|
@@ -479,7 +729,22 @@ async function runView(root, change, runId) {
|
|
|
479
729
|
attempt: r.attempt ?? 1,
|
|
480
730
|
...r.stepClass ? { stepClass: r.stepClass } : {},
|
|
481
731
|
...r.model ? { model: r.model } : {},
|
|
482
|
-
|
|
732
|
+
...r.batch ? { batch: r.batch } : {},
|
|
733
|
+
...r.parts?.length ? { parts: await partStates(root, change, r.parts) } : {},
|
|
734
|
+
promptPath: `${runsRelOf(root)}/${change}/${runId}/prompt.md`,
|
|
735
|
+
...tr ? {
|
|
736
|
+
taskRun: {
|
|
737
|
+
task: tr.task,
|
|
738
|
+
repo: tr.repo,
|
|
739
|
+
level: tr.level,
|
|
740
|
+
state: tr.state,
|
|
741
|
+
branch: tr.branch,
|
|
742
|
+
worktree: tr.worktree,
|
|
743
|
+
notes: tr.notes,
|
|
744
|
+
...tr.commit ? { commit: tr.commit } : {},
|
|
745
|
+
...tr.attempts.at(-1) ? { log: tr.attempts.at(-1).log } : {}
|
|
746
|
+
}
|
|
747
|
+
} : {}
|
|
483
748
|
};
|
|
484
749
|
}
|
|
485
750
|
async function findRun(root, runId) {
|
|
@@ -492,23 +757,137 @@ async function findRun(root, runId) {
|
|
|
492
757
|
}
|
|
493
758
|
throw notFound(`There is no run ${runId}.`);
|
|
494
759
|
}
|
|
495
|
-
async function artifactPath(root, id, artifact) {
|
|
760
|
+
async function artifactPath(root, id, artifact, o = {}) {
|
|
496
761
|
const a = ChangeArtifact.safeParse(artifact);
|
|
497
762
|
if (!a.success)
|
|
498
763
|
throw badRequest(`${artifact} is not an artifact. Use one of ${ChangeArtifact.options.join(", ")}.`);
|
|
499
764
|
const dir = join2(changesDirOf(root), id);
|
|
500
765
|
const files = await artifactFiles(dir, a.data);
|
|
766
|
+
if (o.file) {
|
|
767
|
+
if (!files.includes(o.file))
|
|
768
|
+
throw notFound(`${o.file} isn't one of change ${id}'s ${artifact} files.`);
|
|
769
|
+
return o.file;
|
|
770
|
+
}
|
|
771
|
+
if (files.length === 0 && o.orNew && a.data !== "specs")
|
|
772
|
+
return a.data === "stories" ? "stories.md" : `${a.data}.md`;
|
|
501
773
|
if (files.length === 0)
|
|
502
774
|
throw notFound(`Change ${id} has no ${artifact} yet.`);
|
|
503
775
|
return files[0];
|
|
504
776
|
}
|
|
505
777
|
function uiRoutes(r, ctx, deps = {}) {
|
|
506
778
|
const { root, user, jobs } = ctx;
|
|
779
|
+
const readyView = async (change) => {
|
|
780
|
+
const { deployOrderOf } = await import("./chunk-JKIGL3XQ.js");
|
|
781
|
+
const repoOrder = await deployOrderOf(root, change);
|
|
782
|
+
const prStates = /* @__PURE__ */ new Map();
|
|
783
|
+
for (const t of await taskViews(root, change, {
|
|
784
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {},
|
|
785
|
+
...deps.now ? { now: deps.now } : {}
|
|
786
|
+
}))
|
|
787
|
+
if (t.taskRun?.pr?.state)
|
|
788
|
+
prStates.set(t.id, t.taskRun.pr.state);
|
|
789
|
+
const plan = await readyTasksOf(root, change, {
|
|
790
|
+
...repoOrder ? { repoOrder } : {},
|
|
791
|
+
prStates
|
|
792
|
+
});
|
|
793
|
+
return api_exports.ReadyTasks.parse({ change, ...plan, ...repoOrder ? { repoOrder } : {} });
|
|
794
|
+
};
|
|
795
|
+
const doneBusy = /* @__PURE__ */ new Set();
|
|
507
796
|
const changeDeps = {
|
|
508
797
|
...deps.gitHost ? { gitHost: deps.gitHost } : {},
|
|
509
798
|
...deps.now ? { now: deps.now } : {}
|
|
510
799
|
};
|
|
511
|
-
|
|
800
|
+
const localWhat = /* @__PURE__ */ new Map();
|
|
801
|
+
const asJob = (what, work) => {
|
|
802
|
+
const busy = ctx.jobs.runningOf("local");
|
|
803
|
+
if (busy)
|
|
804
|
+
throw conflict(`${localWhat.get(busy.id) ?? busy.progress.message} is running on this server (${busy.id}): wait for it to finish, then try again.`);
|
|
805
|
+
const job = ctx.jobs.start("local", async (c) => {
|
|
806
|
+
c.progress(0, 1, what);
|
|
807
|
+
const res = await work();
|
|
808
|
+
c.progress(1, 1, res.code === 0 ? "Done" : "Stopped");
|
|
809
|
+
ctx.events.publish({ type: "file.changed", path: "." });
|
|
810
|
+
const lines = [...res.out, ...res.err];
|
|
811
|
+
return { result: { code: res.code, lines }, errors: res.code === 0 ? [] : lines };
|
|
812
|
+
});
|
|
813
|
+
localWhat.set(job.id, what);
|
|
814
|
+
return { status: 202, body: { jobId: job.id } };
|
|
815
|
+
};
|
|
816
|
+
return r.add("POST", "/api/facts/{repo}/apply", async ({ params }) => {
|
|
817
|
+
const { verifyCommand } = await import("./chunk-T74WQQGS.js");
|
|
818
|
+
const repo = params.repo;
|
|
819
|
+
if (!FACTS_REPO.test(repo))
|
|
820
|
+
throw badRequest(`${JSON.stringify(repo)} isn't a repo name.`);
|
|
821
|
+
if (!(await loadRepos(root)).some((x) => x.id === repo))
|
|
822
|
+
throw notFound(`There is no repo ${repo} in this workspace.`);
|
|
823
|
+
return asJob(`Applying to ${repo}`, () => run((io) => verifyCommand(root, repo, { write: true, verbose: false, json: false }, io)));
|
|
824
|
+
}).add("GET", "/api/upgrade", async () => {
|
|
825
|
+
const { upgradeCheck } = await import("./chunk-AM6FP6JW.js");
|
|
826
|
+
const c = await upgradeCheck(root);
|
|
827
|
+
const body = {
|
|
828
|
+
atticus: c.atticus,
|
|
829
|
+
newer: c.newer,
|
|
830
|
+
...c.stamp ? { stamp: c.stamp.atticus } : {},
|
|
831
|
+
pending: c.pending
|
|
832
|
+
};
|
|
833
|
+
return { status: 200, body };
|
|
834
|
+
}).add("POST", "/api/upgrade", async () => {
|
|
835
|
+
const { upgradeCheck, upgradeCommand } = await import("./chunk-AM6FP6JW.js");
|
|
836
|
+
const c = await upgradeCheck(root);
|
|
837
|
+
if (c.newer)
|
|
838
|
+
throw conflict(`This metarepo was upgraded by Atticus ${c.stamp.atticus}; this is ${c.atticus}. Install ${c.stamp.atticus} or later first.`);
|
|
839
|
+
if (!c.pending.length)
|
|
840
|
+
throw conflict(`Nothing to upgrade: up to date for Atticus ${c.atticus}.`);
|
|
841
|
+
return asJob("Upgrading the metarepo", () => run((io) => upgradeCommand(root, { by: user }, io)));
|
|
842
|
+
}).add("GET", "/api/propose", async ({ query }) => {
|
|
843
|
+
const { proposePlan, PROPOSE_SCOPES } = await import("./chunk-UZACEG2C.js");
|
|
844
|
+
const scope = query.get("scope") ?? "";
|
|
845
|
+
if (!PROPOSE_SCOPES.includes(scope))
|
|
846
|
+
throw refused({
|
|
847
|
+
code: 2,
|
|
848
|
+
out: [],
|
|
849
|
+
err: [`scope is one of ${PROPOSE_SCOPES.join(", ")}.`]
|
|
850
|
+
});
|
|
851
|
+
const plan = await proposePlan(root, scope, {
|
|
852
|
+
by: user
|
|
853
|
+
}).catch((e) => {
|
|
854
|
+
throw refused({
|
|
855
|
+
code: 1,
|
|
856
|
+
out: [],
|
|
857
|
+
err: [
|
|
858
|
+
`Atticus can't list what changed here: ${e.message.split("\n")[0]}. Propose for review needs the metarepo to be a Git repository with a remote.`
|
|
859
|
+
]
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
return { status: 200, body: { ...plan, applyFirst: await applyFirstOf(root, plan.files) } };
|
|
863
|
+
}).add("POST", "/api/propose", async ({ body }) => {
|
|
864
|
+
const b = parseBody(api_exports.ProposeRequest, body);
|
|
865
|
+
const { proposeForReview } = await import("./chunk-UZACEG2C.js");
|
|
866
|
+
return asJob("Proposing for review", () => run((io) => proposeForReview(root, {
|
|
867
|
+
scope: b.scope,
|
|
868
|
+
by: user,
|
|
869
|
+
files: b.files,
|
|
870
|
+
...b.title ? { title: b.title } : {},
|
|
871
|
+
...b.branch ? { branch: b.branch } : {}
|
|
872
|
+
}, io)));
|
|
873
|
+
}).add("POST", "/api/propose/bring-in", async () => {
|
|
874
|
+
const { bringInMerged } = await import("./chunk-UZACEG2C.js");
|
|
875
|
+
return asJob("Bringing in what merged", () => run((io) => bringInMerged(root, io)));
|
|
876
|
+
}).add("POST", "/api/changes/{changeId}/conformance", async ({ params }) => {
|
|
877
|
+
await readChange(root, params.changeId);
|
|
878
|
+
const { changeConformCommand } = await import("./chunk-5CMPVC3N.js");
|
|
879
|
+
return asJob(`Checking ${params.changeId}'s code against its design`, () => run((io) => changeConformCommand(root, params.changeId, { findingsAnswer: true }, io)));
|
|
880
|
+
}).add("GET", "/api/change-types", async () => {
|
|
881
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
882
|
+
return {
|
|
883
|
+
status: 200,
|
|
884
|
+
body: op.changeTypes.types.map((t) => ({
|
|
885
|
+
id: t.id,
|
|
886
|
+
name: t.name,
|
|
887
|
+
...t.description ? { description: t.description } : {}
|
|
888
|
+
}))
|
|
889
|
+
};
|
|
890
|
+
}).add("POST", "/api/changes", async ({ body }) => {
|
|
512
891
|
const b = parseBody(NewChange, body);
|
|
513
892
|
const owners = Object.entries(b.owners ?? {}).map(([role, who]) => `${role}=${who.join(",")}`);
|
|
514
893
|
const res = await run((io) => changeNew(root, {
|
|
@@ -530,7 +909,22 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
530
909
|
}).add("GET", "/api/changes/{changeId}", async ({ params }) => ({
|
|
531
910
|
status: 200,
|
|
532
911
|
body: await changeDetail(root, params.changeId)
|
|
533
|
-
})).add("
|
|
912
|
+
})).add("PUT", "/api/changes/{changeId}/owners", async ({ params, body }) => {
|
|
913
|
+
await readChange(root, params.changeId);
|
|
914
|
+
const b = parseBody(api_exports.ChangeOwnersRequest, body);
|
|
915
|
+
try {
|
|
916
|
+
await setChangeOwners(root, params.changeId, b.owners);
|
|
917
|
+
} catch (e) {
|
|
918
|
+
if (e instanceof ChangeOwnersError)
|
|
919
|
+
throw badRequest(e.message);
|
|
920
|
+
throw e;
|
|
921
|
+
}
|
|
922
|
+
ctx.events.publish({
|
|
923
|
+
type: "file.changed",
|
|
924
|
+
path: `${changesRelOf(root)}/${params.changeId}/change.yaml`
|
|
925
|
+
});
|
|
926
|
+
return { status: 200, body: await changeDetail(root, params.changeId) };
|
|
927
|
+
}).add("GET", "/api/changes/{changeId}/status", async ({ params }) => {
|
|
534
928
|
await readChange(root, params.changeId);
|
|
535
929
|
const rep = await changeReport(root, params.changeId);
|
|
536
930
|
return {
|
|
@@ -554,10 +948,13 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
554
948
|
reasons: rep.reasons
|
|
555
949
|
}
|
|
556
950
|
};
|
|
557
|
-
}).add("GET", "/api/changes/{changeId}/artifacts/{artifact}", async ({ params }) => {
|
|
951
|
+
}).add("GET", "/api/changes/{changeId}/artifacts/{artifact}", async ({ params, query }) => {
|
|
558
952
|
await readChange(root, params.changeId);
|
|
559
|
-
const rel = await artifactPath(root, params.changeId, params.artifact
|
|
560
|
-
|
|
953
|
+
const rel = await artifactPath(root, params.changeId, params.artifact, {
|
|
954
|
+
orNew: true,
|
|
955
|
+
file: query.get("file")
|
|
956
|
+
});
|
|
957
|
+
const content = await readFile(join2(changesDirOf(root), params.changeId, rel), "utf8").catch(() => "");
|
|
561
958
|
return {
|
|
562
959
|
status: 200,
|
|
563
960
|
body: {
|
|
@@ -567,15 +964,27 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
567
964
|
sha256: sha(content)
|
|
568
965
|
}
|
|
569
966
|
};
|
|
570
|
-
}).add("PUT", "/api/changes/{changeId}/artifacts/{artifact}", async ({ params, body }) => {
|
|
967
|
+
}).add("PUT", "/api/changes/{changeId}/artifacts/{artifact}", async ({ params, body, query }) => {
|
|
571
968
|
await readChange(root, params.changeId);
|
|
572
969
|
const b = parseBody(api_exports.SaveArtifactRequest, body);
|
|
573
|
-
const rel = await artifactPath(root, params.changeId, params.artifact
|
|
970
|
+
const rel = await artifactPath(root, params.changeId, params.artifact, {
|
|
971
|
+
orNew: true,
|
|
972
|
+
file: query.get("file")
|
|
973
|
+
});
|
|
574
974
|
const file = join2(changesDirOf(root), params.changeId, rel);
|
|
575
|
-
const
|
|
975
|
+
const was = await readFile(file, "utf8").catch(() => void 0);
|
|
976
|
+
const now = was ?? "";
|
|
576
977
|
if (sha(now) !== b.baseSha256)
|
|
577
978
|
throw conflict(`${rel} changed since you opened it. Reload it, then make your edit again.`);
|
|
578
|
-
await
|
|
979
|
+
await mkdir(dirname2(file), { recursive: true });
|
|
980
|
+
await writeFileIfUnchanged(file, was, b.content);
|
|
981
|
+
const { recordPersonSave: recordPersonSave2 } = await import("./chunk-NTAVOWYD.js");
|
|
982
|
+
await recordPersonSave2(join2(changesDirOf(root), params.changeId), params.changeId, {
|
|
983
|
+
file: rel,
|
|
984
|
+
who: user,
|
|
985
|
+
at: (deps.now ? new Date(deps.now) : /* @__PURE__ */ new Date()).toISOString(),
|
|
986
|
+
content: b.content
|
|
987
|
+
}).catch(() => void 0);
|
|
579
988
|
ctx.events.publish({
|
|
580
989
|
type: "file.changed",
|
|
581
990
|
path: `${changesRelOf(root)}/${params.changeId}/${rel}`
|
|
@@ -591,17 +1000,142 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
591
1000
|
};
|
|
592
1001
|
}).add("GET", "/api/changes/{changeId}/tasks", async ({ params }) => {
|
|
593
1002
|
await readChange(root, params.changeId);
|
|
594
|
-
const
|
|
1003
|
+
const change = params.changeId;
|
|
595
1004
|
return {
|
|
596
1005
|
status: 200,
|
|
597
|
-
body:
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
1006
|
+
body: await taskViews(root, change, {
|
|
1007
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {},
|
|
1008
|
+
...deps.now ? { now: deps.now } : {},
|
|
1009
|
+
// Seventh UX walk: a task ticked because its PR merged changed tasks.md.
|
|
1010
|
+
onTick: () => ctx.events.publish({
|
|
1011
|
+
type: "file.changed",
|
|
1012
|
+
path: `${changesRelOf(root)}/${change}/tasks.md`
|
|
1013
|
+
})
|
|
1014
|
+
})
|
|
1015
|
+
};
|
|
1016
|
+
}).add("POST", "/api/changes/{changeId}/tasks/{taskId}/done", async ({ params, body }) => {
|
|
1017
|
+
await readChange(root, params.changeId);
|
|
1018
|
+
const change = params.changeId;
|
|
1019
|
+
const task = params.taskId;
|
|
1020
|
+
const b = parseBody(api_exports.TaskDoneRequest, body);
|
|
1021
|
+
if (!(await taskViews(root, change)).some((t) => t.id === task))
|
|
1022
|
+
throw notFound(`Change ${change} has no task ${task}.`);
|
|
1023
|
+
if (await setTaskDone(root, change, task, b.done))
|
|
1024
|
+
ctx.events.publish({
|
|
1025
|
+
type: "file.changed",
|
|
1026
|
+
path: `${changesRelOf(root)}/${change}/tasks.md`
|
|
1027
|
+
});
|
|
1028
|
+
const tr = await readTaskRun(root, change, task).catch(() => void 0);
|
|
1029
|
+
if (tr) {
|
|
1030
|
+
tr.marked = {
|
|
1031
|
+
done: b.done,
|
|
1032
|
+
by: user,
|
|
1033
|
+
at: (deps.now ? new Date(deps.now) : /* @__PURE__ */ new Date()).toISOString().replace(/\.000Z$/, "Z")
|
|
1034
|
+
};
|
|
1035
|
+
await saveTaskRun(root, tr);
|
|
1036
|
+
}
|
|
1037
|
+
return {
|
|
1038
|
+
status: 200,
|
|
1039
|
+
body: (await taskViews(root, change)).find((t) => t.id === task)
|
|
1040
|
+
};
|
|
1041
|
+
}).add("GET", "/api/changes/{changeId}/tasks/ready", async ({ params }) => {
|
|
1042
|
+
await readChange(root, params.changeId);
|
|
1043
|
+
return { status: 200, body: await readyView(params.changeId) };
|
|
1044
|
+
}).add("POST", "/api/changes/{changeId}/tasks/run-ready", async ({ params, body }) => {
|
|
1045
|
+
const change = params.changeId;
|
|
1046
|
+
await readChange(root, change);
|
|
1047
|
+
const b = parseBody(api_exports.RunReadyRequest, body);
|
|
1048
|
+
const view3 = await readyView(change);
|
|
1049
|
+
const tasks = b.tasks ? view3.ready.filter((t) => b.tasks.includes(t)) : view3.ready;
|
|
1050
|
+
if (!tasks.length)
|
|
1051
|
+
throw refused({
|
|
1052
|
+
code: 1,
|
|
1053
|
+
out: [],
|
|
1054
|
+
err: [
|
|
1055
|
+
b.tasks ? `None of ${b.tasks.join(", ")} is ready to run now: the Task board says why.` : `No task of ${change} is ready to run: each waits for the tasks before it, its PR, the assistant or a person.`
|
|
1056
|
+
]
|
|
1057
|
+
});
|
|
1058
|
+
const repoOf2 = new Map(view3.tasks.map((t) => [t.id, t.repo]));
|
|
1059
|
+
const busy = ctx.jobs.runningOf("step");
|
|
1060
|
+
if (busy)
|
|
1061
|
+
throw conflict(`An AI step is running on this server (${busy.id}): wait for it to finish, or cancel it.`);
|
|
1062
|
+
const job = ctx.jobs.start("step", async (c) => {
|
|
1063
|
+
const r2 = await runReadyTasks(root, change, {
|
|
1064
|
+
by: user,
|
|
1065
|
+
only: tasks,
|
|
1066
|
+
...view3.repoOrder ? { repoOrder: view3.repoOrder } : {},
|
|
1067
|
+
...b.level ? { level: b.level } : {},
|
|
1068
|
+
...b.runtime ? { mode: b.runtime } : {},
|
|
1069
|
+
// As Run with AI: runtime.yaml's level, lowered to the repo's cap, and at most draft.
|
|
1070
|
+
levelFor: async (task) => {
|
|
1071
|
+
const p = await previewTaskRun(root, change, task, {
|
|
1072
|
+
skipGitHost: true,
|
|
1073
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {}
|
|
1074
|
+
});
|
|
1075
|
+
return p.level === "pr" ? "draft" : p.level;
|
|
1076
|
+
},
|
|
1077
|
+
// The task's repo gets its worktree when it has none yet, as Run with AI does.
|
|
1078
|
+
before: async (task) => {
|
|
1079
|
+
const opened = await openTaskWorktree(root, change, task, deps.gitHost);
|
|
1080
|
+
return opened && opened.code !== 0 ? opened.err.join(" ") || opened.out.join(" ") || "Its worktree couldn't be opened." : void 0;
|
|
1081
|
+
},
|
|
1082
|
+
progress: (done, total, message) => c.progress(done, total, message),
|
|
1083
|
+
cancelled: () => c.cancelled,
|
|
1084
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {},
|
|
1085
|
+
...deps.taskRun?.git ? { git: deps.taskRun.git } : {},
|
|
1086
|
+
...deps.taskRun?.commands ? { commands: deps.taskRun.commands } : {}
|
|
1087
|
+
});
|
|
1088
|
+
for (const x of r2.ran)
|
|
1089
|
+
ctx.events.publish({
|
|
1090
|
+
type: "file.changed",
|
|
1091
|
+
path: `${changesRelOf(root)}/${change}/tasks/${x.task}/task-run.yaml`
|
|
1092
|
+
});
|
|
1093
|
+
const ran = r2.ran.map((x) => "record" in x ? { task: x.task, repo: x.repo, state: x.record.state, branch: x.record.branch } : { task: x.task, repo: x.repo || (repoOf2.get(x.task) ?? ""), refused: x.refused });
|
|
1094
|
+
const left = r2.stopped ? r2.planned.slice(r2.planned.indexOf(r2.stopped.task) + 1) : [];
|
|
1095
|
+
return {
|
|
1096
|
+
result: api_exports.RunReadyResult.parse({
|
|
1097
|
+
ran,
|
|
1098
|
+
...r2.stopped ? { stopped: r2.stopped } : {},
|
|
1099
|
+
left
|
|
1100
|
+
})
|
|
1101
|
+
};
|
|
1102
|
+
});
|
|
1103
|
+
return { status: 202, body: api_exports.RunReadyAccepted.parse({ jobId: job.id, tasks }) };
|
|
1104
|
+
}).add("POST", "/api/changes/{changeId}/tasks/{taskId}/pr", async ({ params }) => {
|
|
1105
|
+
await readChange(root, params.changeId);
|
|
1106
|
+
const change = params.changeId;
|
|
1107
|
+
const task = params.taskId;
|
|
1108
|
+
if (!(await taskViews(root, change)).some((t) => t.id === task))
|
|
1109
|
+
throw notFound(`Change ${change} has no task ${task}.`);
|
|
1110
|
+
let raised;
|
|
1111
|
+
try {
|
|
1112
|
+
raised = await raiseTaskPr(root, change, task, {
|
|
1113
|
+
approval: {
|
|
1114
|
+
by: user,
|
|
1115
|
+
at: (deps.now ? new Date(deps.now) : /* @__PURE__ */ new Date()).toISOString(),
|
|
1116
|
+
ref: `task ${task} of ${change}: push and PR (the app's Raise the task's PR)`
|
|
1117
|
+
},
|
|
1118
|
+
...deps.taskRun?.git ? { git: deps.taskRun.git } : {},
|
|
1119
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {}
|
|
1120
|
+
});
|
|
1121
|
+
} catch (e) {
|
|
1122
|
+
if (e instanceof Error && (e.name === "TaskRunError" || TASK_ERRORS.has(e.name)))
|
|
1123
|
+
throw refused({ code: 1, out: [], err: [e.message] });
|
|
1124
|
+
throw e;
|
|
1125
|
+
}
|
|
1126
|
+
ctx.events.publish({
|
|
1127
|
+
type: "file.changed",
|
|
1128
|
+
path: `${changesRelOf(root)}/${change}/tasks/${task}/task-run.yaml`
|
|
1129
|
+
});
|
|
1130
|
+
if (raised.state !== "pr-open")
|
|
1131
|
+
throw refused({
|
|
1132
|
+
code: 1,
|
|
1133
|
+
out: [],
|
|
1134
|
+
err: [raised.notes.at(-1) ?? "The PR wasn't raised."]
|
|
1135
|
+
});
|
|
1136
|
+
return {
|
|
1137
|
+
status: 201,
|
|
1138
|
+
body: (await taskViews(root, change, { ...deps.gitHost ? { gitHost: deps.gitHost } : {} })).find((t) => t.id === task)
|
|
605
1139
|
};
|
|
606
1140
|
}).add("POST", "/api/changes/{changeId}/workspace", async ({ params, body }) => {
|
|
607
1141
|
await readChange(root, params.changeId);
|
|
@@ -617,41 +1151,69 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
617
1151
|
const res = await run((io) => changeHandoff(root, params.changeId, { by: user, approve: true, ...b.repo ? { repo: b.repo } : {} }, io, changeDeps));
|
|
618
1152
|
if (res.code !== 0)
|
|
619
1153
|
throw refused(res);
|
|
620
|
-
return {
|
|
1154
|
+
return {
|
|
1155
|
+
status: 201,
|
|
1156
|
+
body: { ...await changeDetail(root, params.changeId), notes: res.out }
|
|
1157
|
+
};
|
|
1158
|
+
}).add("POST", "/api/changes/{changeId}/release-prs", async ({ params, body }) => {
|
|
1159
|
+
const id = params.changeId;
|
|
1160
|
+
await readChange(root, id);
|
|
1161
|
+
parseBody(api_exports.ReleasePrsRequest, body);
|
|
1162
|
+
const res = await run((io) => changeReleasePrs(root, id, { by: user, approve: true }, io, changeDeps));
|
|
1163
|
+
ctx.events.publish({ type: "file.changed", path: `${id}/repos.yaml` });
|
|
1164
|
+
if (res.code !== 0)
|
|
1165
|
+
throw refused(res);
|
|
1166
|
+
return { status: 201, body: api_exports.LinesView.parse({ lines: res.out }) };
|
|
621
1167
|
}).add("POST", "/api/changes/{changeId}/sync", async ({ params, body }) => {
|
|
622
1168
|
await readChange(root, params.changeId);
|
|
623
1169
|
const b = parseBody(api_exports.SyncRequest, body);
|
|
624
1170
|
const res = await run((io) => changeSync(root, params.changeId, { by: user, approve: true, ...b.repo ? { repo: b.repo } : {} }, io, changeDeps));
|
|
625
1171
|
if (res.code === 2)
|
|
626
1172
|
throw refused(res);
|
|
1173
|
+
const { appLines } = await import("./chunk-J2O4LFVI.js");
|
|
627
1174
|
return {
|
|
628
1175
|
status: 200,
|
|
629
1176
|
body: {
|
|
630
1177
|
...await changeDetail(root, params.changeId),
|
|
631
|
-
lines: [...res.out, ...res.err]
|
|
1178
|
+
lines: appLines([...res.out, ...res.err])
|
|
632
1179
|
}
|
|
633
1180
|
};
|
|
634
1181
|
}).add("GET", "/api/changes/{changeId}/inputs", async ({ params }) => {
|
|
635
1182
|
const c = await readChange(root, params.changeId);
|
|
636
|
-
const { readChangeInputs } = await import("./chunk-
|
|
1183
|
+
const { readChangeInputs } = await import("./chunk-UDXWIPCB.js");
|
|
637
1184
|
return {
|
|
638
1185
|
status: 200,
|
|
639
1186
|
body: api_exports.ChangeInputList.parse(await readChangeInputs(join2(changesDirOf(root), c.id)))
|
|
640
1187
|
};
|
|
641
1188
|
}).add("GET", "/api/changes/{changeId}/prototypes", async ({ params }) => {
|
|
642
1189
|
const c = await readChange(root, params.changeId);
|
|
643
|
-
const { changePrototypes } = await import("./chunk-
|
|
1190
|
+
const { changePrototypes } = await import("./chunk-VYOCPTUB.js");
|
|
644
1191
|
return {
|
|
645
1192
|
status: 200,
|
|
646
1193
|
body: api_exports.ChangePrototypeList.parse(await changePrototypes(root, c.id))
|
|
647
1194
|
};
|
|
1195
|
+
}).add("POST", "/api/changes/{changeId}/prototypes", async ({ params, body }) => {
|
|
1196
|
+
const c = await readChange(root, params.changeId);
|
|
1197
|
+
const b = parseBody(api_exports.ComparePrototypeRequest, body);
|
|
1198
|
+
const { attachPrototype, changePrototypes } = await import("./chunk-VYOCPTUB.js");
|
|
1199
|
+
const res = await run((io) => attachPrototype(root, c.id, b.repo, { by: user, ...deps.now ? { now: new Date(deps.now) } : {} }, io));
|
|
1200
|
+
if (res.code !== 0)
|
|
1201
|
+
throw refused(res);
|
|
1202
|
+
ctx.events.publish({ type: "file.changed", path: `${changesRelOf(root)}/${c.id}/inputs` });
|
|
1203
|
+
return {
|
|
1204
|
+
status: 201,
|
|
1205
|
+
body: api_exports.ComparePrototypeView.parse({
|
|
1206
|
+
...await changePrototypes(root, c.id),
|
|
1207
|
+
lines: res.out
|
|
1208
|
+
})
|
|
1209
|
+
};
|
|
648
1210
|
}).add("POST", "/api/changes/{changeId}/inputs", async ({ params, body }) => {
|
|
649
1211
|
const c = await readChange(root, params.changeId);
|
|
650
1212
|
const b = parseBody(api_exports.AttachFileRequest, body);
|
|
651
1213
|
const bytes = new Uint8Array(Buffer.from(b.content, "base64"));
|
|
652
1214
|
if (bytes.length > MAX_ATTACH)
|
|
653
1215
|
throw new HttpError(413, "The file is too large", "Attach a file of 15 MB or less.");
|
|
654
|
-
const { attachFiles } = await import("./chunk-
|
|
1216
|
+
const { attachFiles } = await import("./chunk-XQ3YZ4QW.js");
|
|
655
1217
|
const r2 = await attachFiles(root, c.id, [{ name: b.name, bytes }], user);
|
|
656
1218
|
if (!r2.added.length)
|
|
657
1219
|
throw new HttpError(422, "Atticus can't attach that", r2.refused[0]?.why ?? "", {
|
|
@@ -673,15 +1235,42 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
673
1235
|
const b = parseBody(api_exports.StartRunRequest, body);
|
|
674
1236
|
const before = new Set(await readdir(join2(runsDirOf(root), params.changeId)).catch(() => []));
|
|
675
1237
|
let res;
|
|
676
|
-
if (b.task)
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1238
|
+
if (b.task) {
|
|
1239
|
+
let level = b.autonomy ? b.autonomy === "draft-and-pr" ? "pr" : b.autonomy === "plan-only" ? "plan" : "draft" : void 0;
|
|
1240
|
+
if (level === "pr")
|
|
1241
|
+
throw refused({
|
|
1242
|
+
code: 1,
|
|
1243
|
+
out: [],
|
|
1244
|
+
err: [
|
|
1245
|
+
"The pr level pushes the task branch and opens a PR, which needs the person's yes: start it from VS Code, IntelliJ or the command line (atticus task run <change> <task> --level pr --approve)."
|
|
1246
|
+
]
|
|
1247
|
+
});
|
|
1248
|
+
if (!level) {
|
|
1249
|
+
try {
|
|
1250
|
+
const p = await previewTaskRun(root, params.changeId, b.task, {
|
|
1251
|
+
skipGitHost: true,
|
|
1252
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {}
|
|
1253
|
+
});
|
|
1254
|
+
level = p.level === "pr" ? "draft" : p.level;
|
|
1255
|
+
} catch (e) {
|
|
1256
|
+
if (e instanceof Error && e.name === "TaskRunError")
|
|
1257
|
+
throw conflict(e.message);
|
|
1258
|
+
if (e instanceof Error && TASK_ERRORS.has(e.name))
|
|
1259
|
+
throw refused({ code: 1, out: [], err: [e.message] });
|
|
1260
|
+
throw e;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
const lv = level;
|
|
1264
|
+
const opened = await openTaskWorktree(root, params.changeId, b.task, deps.gitHost);
|
|
1265
|
+
if (opened && opened.code !== 0)
|
|
1266
|
+
throw refused(opened);
|
|
1267
|
+
res = await run((io) => taskRun(root, params.changeId, b.task, { mode: b.runtime, by: user, level: lv }, io, {
|
|
1268
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {},
|
|
1269
|
+
...deps.taskRun?.git ? { git: deps.taskRun.git } : {}
|
|
1270
|
+
}));
|
|
1271
|
+
if (opened)
|
|
1272
|
+
res = { ...res, out: [...opened.out, ...res.out] };
|
|
1273
|
+
} else if (b.agent === "story-writer")
|
|
685
1274
|
res = await run((io) => planStories(root, params.changeId, { mode: b.runtime }, io));
|
|
686
1275
|
else if (b.agent.endsWith("-critic"))
|
|
687
1276
|
res = await run((io) => critic(root, params.changeId, { mode: b.runtime }, io));
|
|
@@ -732,32 +1321,110 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
732
1321
|
const before = await findRun(root, params.runId);
|
|
733
1322
|
if (before.state === "validated" || before.state === "cancelled")
|
|
734
1323
|
throw conflict(`Run ${params.runId} is already ${before.state}.`);
|
|
735
|
-
|
|
1324
|
+
if (doneBusy.has(params.runId))
|
|
1325
|
+
throw conflict(`Done for ${params.runId} is already running; wait for it to finish.`);
|
|
1326
|
+
doneBusy.add(params.runId);
|
|
1327
|
+
let res;
|
|
1328
|
+
try {
|
|
1329
|
+
res = await run((io) => runDone(root, params.runId, { by: user }, io, deps.taskRun));
|
|
1330
|
+
} finally {
|
|
1331
|
+
doneBusy.delete(params.runId);
|
|
1332
|
+
}
|
|
736
1333
|
const after = await findRun(root, params.runId);
|
|
737
1334
|
ctx.events.publish({ type: "run.changed", runId: after.runId, state: after.state });
|
|
738
|
-
|
|
1335
|
+
const { resumeAfterDone, appLines } = await import("./chunk-J2O4LFVI.js");
|
|
1336
|
+
if (res.code !== 0)
|
|
1337
|
+
throw refused({ ...res, out: appLines(res.out), err: appLines(res.err) });
|
|
1338
|
+
const stepRunner = deps.runStep ?? (async (args) => {
|
|
1339
|
+
const { main } = await import("./chunk-7FHNFCHK.js");
|
|
1340
|
+
const out = [];
|
|
1341
|
+
const err = [];
|
|
1342
|
+
const code = await main([...args, "--root", root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
|
|
1343
|
+
return { code, out, err };
|
|
1344
|
+
});
|
|
1345
|
+
const resumed = await resumeAfterDone(root, stepRunner, after.change, after.runId).catch((e) => ({ lines: [e.message] }));
|
|
1346
|
+
if (resumed)
|
|
1347
|
+
ctx.events.publish({ type: "file.changed", path: changesRelOf(root) });
|
|
1348
|
+
return {
|
|
1349
|
+
status: 200,
|
|
1350
|
+
body: {
|
|
1351
|
+
...after,
|
|
1352
|
+
// When the app resumed the step, runDone's "run the step again with --resume" is done;
|
|
1353
|
+
// sixth UX walk: what is left to do is said in the app's words.
|
|
1354
|
+
lines: [
|
|
1355
|
+
...resumed ? appLines([...res.out, ...res.err].filter((l) => !/--resume run-/.test(l))) : [...res.out, ...res.err],
|
|
1356
|
+
...resumed?.lines ?? []
|
|
1357
|
+
],
|
|
1358
|
+
...resumed?.next ? { next: resumed.next } : {}
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
739
1361
|
}).add("POST", "/api/runs/{runId}/retry", async ({ params }) => {
|
|
740
1362
|
const v = await findRun(root, params.runId);
|
|
741
1363
|
if (v.state !== "failed")
|
|
742
1364
|
throw conflict(`Only a failed run can be retried; ${v.runId} is ${v.state}.`);
|
|
1365
|
+
const owner = await findTaskRunOfRun(root, v.runId);
|
|
1366
|
+
if (owner && owner.waiting !== v.runId)
|
|
1367
|
+
throw conflict(taskRunNotWaiting(v.runId, owner));
|
|
743
1368
|
const file = join2(runsDirOf(root), v.change, v.runId, "run.json");
|
|
744
|
-
const
|
|
1369
|
+
const was = await readFile(file, "utf8");
|
|
1370
|
+
const rec = JSON.parse(was);
|
|
745
1371
|
rec.state = "prepared";
|
|
746
1372
|
rec.attempt = (rec.attempt ?? 1) + 1;
|
|
747
1373
|
rec.errors = [];
|
|
748
|
-
await
|
|
1374
|
+
await writeFileIfUnchanged(file, was, JSON.stringify(rec, null, 2) + "\n");
|
|
749
1375
|
const after = await findRun(root, params.runId);
|
|
750
1376
|
ctx.events.publish({ type: "run.changed", runId: after.runId, state: after.state });
|
|
751
1377
|
return { status: 200, body: after };
|
|
752
1378
|
}).add("POST", "/api/runs/{runId}/cancel", async ({ params }) => {
|
|
1379
|
+
const here = await findRun(root, params.runId).catch(() => void 0);
|
|
1380
|
+
if (!here) {
|
|
1381
|
+
let ended;
|
|
1382
|
+
try {
|
|
1383
|
+
ended = await cancelTaskRunWait(root, params.runId, { by: user });
|
|
1384
|
+
} catch (e) {
|
|
1385
|
+
if (e instanceof Error && e.name === "TaskRunError")
|
|
1386
|
+
throw conflict(e.message);
|
|
1387
|
+
throw e;
|
|
1388
|
+
}
|
|
1389
|
+
if (!ended)
|
|
1390
|
+
throw notFound(`There is no run ${params.runId}.`);
|
|
1391
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1392
|
+
const body = {
|
|
1393
|
+
schemaVersion: RUN_SCHEMA_VERSION,
|
|
1394
|
+
runId: params.runId,
|
|
1395
|
+
change: ended.change,
|
|
1396
|
+
agent: "task-runner",
|
|
1397
|
+
runtime: "hand-off",
|
|
1398
|
+
state: "cancelled",
|
|
1399
|
+
startedAt: ended.startedAt,
|
|
1400
|
+
finishedAt: at,
|
|
1401
|
+
contextManifestSha256: sha(""),
|
|
1402
|
+
errors: [],
|
|
1403
|
+
attempt: 1
|
|
1404
|
+
};
|
|
1405
|
+
ctx.events.publish({ type: "run.changed", runId: params.runId, state: "cancelled" });
|
|
1406
|
+
return { status: 200, body };
|
|
1407
|
+
}
|
|
753
1408
|
const v = await findRun(root, params.runId);
|
|
754
1409
|
if (["validated", "cancelled"].includes(v.state))
|
|
755
1410
|
throw conflict(`Run ${v.runId} is already ${v.state}.`);
|
|
1411
|
+
if (doneBusy.has(v.runId))
|
|
1412
|
+
throw conflict(`Done for ${v.runId} is running; wait for it to finish.`);
|
|
756
1413
|
const file = join2(runsDirOf(root), v.change, v.runId, "run.json");
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
1414
|
+
const cancelRun = async () => {
|
|
1415
|
+
const was = await readFile(file, "utf8");
|
|
1416
|
+
const rec = JSON.parse(was);
|
|
1417
|
+
rec.state = "cancelled";
|
|
1418
|
+
rec.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1419
|
+
await writeFileIfUnchanged(file, was, JSON.stringify(rec, null, 2) + "\n");
|
|
1420
|
+
};
|
|
1421
|
+
try {
|
|
1422
|
+
await cancelTaskRunWait(root, v.runId, { by: user, cancelRun });
|
|
1423
|
+
} catch (e) {
|
|
1424
|
+
if (e instanceof Error && e.name === "TaskRunError")
|
|
1425
|
+
throw conflict(e.message);
|
|
1426
|
+
throw e;
|
|
1427
|
+
}
|
|
761
1428
|
const after = await findRun(root, params.runId);
|
|
762
1429
|
ctx.events.publish({ type: "run.changed", runId: after.runId, state: after.state });
|
|
763
1430
|
return { status: 200, body: after };
|
|
@@ -840,8 +1507,10 @@ function uiRoutes(r, ctx, deps = {}) {
|
|
|
840
1507
|
}
|
|
841
1508
|
|
|
842
1509
|
// packages/cli/dist/ui-api-run.js
|
|
843
|
-
var
|
|
844
|
-
import {
|
|
1510
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
1511
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1512
|
+
import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
1513
|
+
import { homedir } from "node:os";
|
|
845
1514
|
import { join as join3, resolve } from "node:path";
|
|
846
1515
|
async function run2(f) {
|
|
847
1516
|
const out = [];
|
|
@@ -862,11 +1531,11 @@ function parseBody2(schema, body) {
|
|
|
862
1531
|
return r.data;
|
|
863
1532
|
}
|
|
864
1533
|
function jsonOf(r) {
|
|
865
|
-
const
|
|
866
|
-
if (!
|
|
1534
|
+
const text = r.out.join("\n").trim();
|
|
1535
|
+
if (!text.startsWith("{") && !text.startsWith("["))
|
|
867
1536
|
return void 0;
|
|
868
1537
|
try {
|
|
869
|
-
return JSON.parse(
|
|
1538
|
+
return JSON.parse(text);
|
|
870
1539
|
} catch {
|
|
871
1540
|
return void 0;
|
|
872
1541
|
}
|
|
@@ -898,7 +1567,7 @@ async function latestFindings2(root, change) {
|
|
|
898
1567
|
}
|
|
899
1568
|
return out;
|
|
900
1569
|
}
|
|
901
|
-
async function
|
|
1570
|
+
async function filesUnder2(dir, prefix = "") {
|
|
902
1571
|
const out = [];
|
|
903
1572
|
let entries;
|
|
904
1573
|
try {
|
|
@@ -909,9 +1578,9 @@ async function filesUnder(dir, prefix = "") {
|
|
|
909
1578
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
910
1579
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
911
1580
|
if (e.isDirectory())
|
|
912
|
-
out.push(...await
|
|
1581
|
+
out.push(...await filesUnder2(join3(dir, e.name), rel));
|
|
913
1582
|
else
|
|
914
|
-
out.push({ path: rel, bytes: (await
|
|
1583
|
+
out.push({ path: rel, bytes: (await stat2(join3(dir, e.name))).size });
|
|
915
1584
|
}
|
|
916
1585
|
return out;
|
|
917
1586
|
}
|
|
@@ -971,6 +1640,8 @@ async function findStep(root, id, change) {
|
|
|
971
1640
|
throw notFound(`There is no step ${stepRef(id, change)}.`);
|
|
972
1641
|
return s;
|
|
973
1642
|
}
|
|
1643
|
+
var sha2 = (s) => createHash2("sha256").update(s).digest("hex");
|
|
1644
|
+
var snowText = (dir) => readFile2(join3(dir, "snow.md"), "utf8").catch(() => "");
|
|
974
1645
|
var SyncEntries = external_exports.object({
|
|
975
1646
|
requests: external_exports.array(external_exports.object({ key: external_exports.string(), id: external_exports.string() })).default([])
|
|
976
1647
|
});
|
|
@@ -979,9 +1650,13 @@ async function changeRequests(root, change, templates, steps) {
|
|
|
979
1650
|
const drafts = readSnowDrafts(await readChangeFiles(dir));
|
|
980
1651
|
if (drafts.length === 0)
|
|
981
1652
|
return [];
|
|
1653
|
+
const snow = {
|
|
1654
|
+
file: `${changesRelOf(root)}/${change}/snow.md`,
|
|
1655
|
+
sha256: sha2(await snowText(dir))
|
|
1656
|
+
};
|
|
982
1657
|
let synced = [];
|
|
983
1658
|
try {
|
|
984
|
-
synced = SyncEntries.parse((0,
|
|
1659
|
+
synced = SyncEntries.parse((0, import_yaml2.parse)(await readFile2(join3(dir, SYNC_FILE), "utf8"))).requests;
|
|
985
1660
|
} catch {
|
|
986
1661
|
}
|
|
987
1662
|
return drafts.map((d) => {
|
|
@@ -1001,7 +1676,8 @@ async function changeRequests(root, change, templates, steps) {
|
|
|
1001
1676
|
record,
|
|
1002
1677
|
approvers: t?.approvers ?? [],
|
|
1003
1678
|
fields,
|
|
1004
|
-
missing: fields.filter((f) => f.required && !f.value.trim()).map((f) => f.name)
|
|
1679
|
+
missing: fields.filter((f) => f.required && !f.value.trim()).map((f) => f.name),
|
|
1680
|
+
...snow
|
|
1005
1681
|
};
|
|
1006
1682
|
const entry = t ? synced.find((e) => e.key === `${t.id}|${record}`) : void 0;
|
|
1007
1683
|
if (!entry)
|
|
@@ -1052,6 +1728,23 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1052
1728
|
const nowDate = deps.now ? () => new Date(deps.now) : void 0;
|
|
1053
1729
|
const pack = () => loadOrgPack(orgPackDirOf(root));
|
|
1054
1730
|
const rel = deps.now ? { now: () => new Date(deps.now) } : {};
|
|
1731
|
+
const syncAfterStep = async (s) => {
|
|
1732
|
+
if (s.system !== "git-host" || !s.change || s.state === "open")
|
|
1733
|
+
return void 0;
|
|
1734
|
+
const c = await changeStore(root).tryRead(s.change).catch(() => void 0);
|
|
1735
|
+
const live = c && c.state !== "returned" ? liveHandoff(c) : void 0;
|
|
1736
|
+
const steps = await fileStepStore(root).list({ change: s.change, system: "git-host" });
|
|
1737
|
+
const open = s.action === "open-pull-request" ? s : steps.find((x) => x.action === "open-pull-request" && s.dependsOn.includes(x.id));
|
|
1738
|
+
if (!live || !open || open.inputs.head !== live.branch)
|
|
1739
|
+
return void 0;
|
|
1740
|
+
const repo = typeof open.inputs.repo === "string" ? open.inputs.repo : void 0;
|
|
1741
|
+
const res = await run2((io) => changeSync(root, s.change, { by: user, ...repo ? { repo } : {} }, io, {
|
|
1742
|
+
...deps.gitHost ? { gitHost: deps.gitHost } : {},
|
|
1743
|
+
...deps.now ? { now: deps.now } : {}
|
|
1744
|
+
}));
|
|
1745
|
+
ctx.events.publish({ type: "file.changed", path: `${changesRelOf(root)}/${s.change}` });
|
|
1746
|
+
return [...res.out, ...res.err];
|
|
1747
|
+
};
|
|
1055
1748
|
return r.add("GET", "/api/todo", async ({ query }) => {
|
|
1056
1749
|
const change = strParam(query, "change");
|
|
1057
1750
|
const op = await pack();
|
|
@@ -1126,7 +1819,8 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1126
1819
|
}
|
|
1127
1820
|
ctx.events.publish({ type: "file.changed", path: `steps/${params.stepId}` });
|
|
1128
1821
|
const s = await findStep(root, params.stepId, b.change);
|
|
1129
|
-
|
|
1822
|
+
const synced = await syncAfterStep(s);
|
|
1823
|
+
return { status: 200, body: { ...s, lines: res.out, ...synced ? { synced } : {} } };
|
|
1130
1824
|
}).add("GET", "/api/todo/{stepId}/run", async ({ params, query }) => {
|
|
1131
1825
|
const s = await findStep(root, params.stepId, strParam(query, "change"));
|
|
1132
1826
|
const op = await pack();
|
|
@@ -1199,12 +1893,44 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1199
1893
|
throw refused2(res);
|
|
1200
1894
|
ctx.events.publish({ type: "file.changed", path: `${params.changeId}/snow.md` });
|
|
1201
1895
|
return { status: 200, body: { lines: res.out, toFill: res.code === 1 } };
|
|
1202
|
-
}).add("
|
|
1203
|
-
await readChange2(root, params.changeId);
|
|
1204
|
-
const
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1896
|
+
}).add("PUT", "/api/service-desk/{changeId}/fields", async ({ params, body }) => {
|
|
1897
|
+
const c = await readChange2(root, params.changeId);
|
|
1898
|
+
const b = parseBody2(api_exports.FillRequestFieldsRequest, body);
|
|
1899
|
+
const dir = changeFolder(root, c.id);
|
|
1900
|
+
const rel2 = `${changesRelOf(root)}/${c.id}/snow.md`;
|
|
1901
|
+
const was = await readFile2(join3(dir, "snow.md"), "utf8").catch(() => void 0);
|
|
1902
|
+
if (was === void 0)
|
|
1903
|
+
throw notFound(`${c.id} has no snow.md: draft its requests first.`);
|
|
1904
|
+
if (sha2(was) !== b.baseSha256)
|
|
1905
|
+
throw conflict(`${rel2} changed since you opened it (another edit, or a person's): reload the page and fill the fields again.`);
|
|
1906
|
+
let next;
|
|
1907
|
+
try {
|
|
1908
|
+
next = fillSnowFields(was, b.heading, b.values);
|
|
1909
|
+
} catch (e) {
|
|
1910
|
+
if (e instanceof SnowFillError)
|
|
1911
|
+
throw new HttpError(422, "Atticus can't fill that", e.message);
|
|
1912
|
+
throw e;
|
|
1913
|
+
}
|
|
1914
|
+
await writeFileIfUnchanged(join3(dir, "snow.md"), was, next);
|
|
1915
|
+
ctx.events.publish({ type: "file.changed", path: rel2 });
|
|
1916
|
+
const op = await pack();
|
|
1917
|
+
const now = (await changeRequests(root, c.id, op.serviceRequests, [])).find((r2) => r2.heading === b.heading);
|
|
1918
|
+
const names = Object.keys(b.values).join(", ");
|
|
1919
|
+
return {
|
|
1920
|
+
status: 200,
|
|
1921
|
+
body: {
|
|
1922
|
+
path: rel2,
|
|
1923
|
+
sha256: sha2(next),
|
|
1924
|
+
missing: now?.missing ?? [],
|
|
1925
|
+
lines: [`Filled ${names} in ${rel2}.`]
|
|
1926
|
+
}
|
|
1927
|
+
};
|
|
1928
|
+
}).add("POST", "/api/service-desk/{changeId}/submit", async ({ params }) => {
|
|
1929
|
+
await readChange2(root, params.changeId);
|
|
1930
|
+
const op = await pack();
|
|
1931
|
+
const desk = deps.serviceDesk ?? manualAdapter("serviceDesk", {
|
|
1932
|
+
store: fileStepStore(root),
|
|
1933
|
+
root,
|
|
1208
1934
|
change: params.changeId,
|
|
1209
1935
|
procedures: op.procedures,
|
|
1210
1936
|
...deps.now ? { now: () => deps.now } : {}
|
|
@@ -1293,7 +2019,7 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1293
2019
|
} : {},
|
|
1294
2020
|
conflicts,
|
|
1295
2021
|
archive: { pass: archive.pass, blockers: archive.blockers, notes: archive.notes },
|
|
1296
|
-
evidence: await
|
|
2022
|
+
evidence: await filesUnder2(changeFolder(root, id))
|
|
1297
2023
|
}
|
|
1298
2024
|
};
|
|
1299
2025
|
}).add("POST", "/api/release/{changeId}/archive", async ({ params, body }) => {
|
|
@@ -1470,7 +2196,7 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1470
2196
|
const { lessons, problems } = await lessonsFor(root);
|
|
1471
2197
|
const sandbox = await isSandbox(root);
|
|
1472
2198
|
const progress = sandbox ? await readProgress(root) : void 0;
|
|
1473
|
-
const
|
|
2199
|
+
const view3 = lessons.map((l) => {
|
|
1474
2200
|
const p = progress?.lessons[l.id];
|
|
1475
2201
|
const done = p ? l.steps.filter((s) => p.steps[s.id]).length : 0;
|
|
1476
2202
|
return {
|
|
@@ -1486,8 +2212,8 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1486
2212
|
});
|
|
1487
2213
|
const roles = [...new Set(lessons.map((l) => l.role))].map((role) => ({
|
|
1488
2214
|
role,
|
|
1489
|
-
lessons:
|
|
1490
|
-
done:
|
|
2215
|
+
lessons: view3.filter((v) => v.role === role).length,
|
|
2216
|
+
done: view3.filter((v) => v.role === role && v.state === "done").length
|
|
1491
2217
|
}));
|
|
1492
2218
|
const cur = progress?.current ? lessons.find((l) => l.id === progress.current) : void 0;
|
|
1493
2219
|
const lp = cur ? progress.lessons[cur.id] : void 0;
|
|
@@ -1497,7 +2223,7 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1497
2223
|
body: {
|
|
1498
2224
|
sandbox,
|
|
1499
2225
|
roles,
|
|
1500
|
-
lessons:
|
|
2226
|
+
lessons: view3,
|
|
1501
2227
|
...cur ? {
|
|
1502
2228
|
current: {
|
|
1503
2229
|
lesson: cur.id,
|
|
@@ -1537,7 +2263,8 @@ function uiRunRoutes(r, ctx, deps = {}) {
|
|
|
1537
2263
|
return { status: 200, body: { ...out, ...hint ? { hint } : {} } };
|
|
1538
2264
|
}).add("POST", "/api/learning/sandbox", async ({ body }) => {
|
|
1539
2265
|
const b = parseBody2(api_exports.SandboxRequest, body);
|
|
1540
|
-
const
|
|
2266
|
+
const dir = b.dir.replace(/^~(?=$|[\\/])/, homedir());
|
|
2267
|
+
const res = await run2((io) => sandboxCreate(resolve(root, dir), { json: true }, io, nowDate ? { now: nowDate() } : {}));
|
|
1541
2268
|
const out = jsonOf(res);
|
|
1542
2269
|
if (res.code !== 0 || !out)
|
|
1543
2270
|
throw refused2(res);
|
|
@@ -1709,10 +2436,10 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1709
2436
|
let checked;
|
|
1710
2437
|
const machine = (fresh = false) => {
|
|
1711
2438
|
if (fresh || !checked || Date.now() - checked.at > 3e5) {
|
|
1712
|
-
const
|
|
1713
|
-
checked =
|
|
1714
|
-
|
|
1715
|
-
if (checked ===
|
|
2439
|
+
const run4 = { at: Date.now(), checks: doctor(ctx.root) };
|
|
2440
|
+
checked = run4;
|
|
2441
|
+
run4.checks.catch(() => {
|
|
2442
|
+
if (checked === run4)
|
|
1716
2443
|
checked = void 0;
|
|
1717
2444
|
});
|
|
1718
2445
|
}
|
|
@@ -1720,7 +2447,7 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1720
2447
|
};
|
|
1721
2448
|
if (o.warm)
|
|
1722
2449
|
void machine().catch(() => void 0);
|
|
1723
|
-
const
|
|
2450
|
+
const view3 = async (name, arg) => {
|
|
1724
2451
|
const build = VIEWS[name];
|
|
1725
2452
|
if (!build)
|
|
1726
2453
|
throw notFound(`There is no view ${name}.`);
|
|
@@ -1729,11 +2456,11 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1729
2456
|
const args = build(arg);
|
|
1730
2457
|
if (!args)
|
|
1731
2458
|
throw badRequest(`The ${name} view needs an id: /api/view/${name}/<id>.`);
|
|
1732
|
-
const { main } = await import("./chunk-
|
|
2459
|
+
const { main } = await import("./chunk-7FHNFCHK.js");
|
|
1733
2460
|
const out = [];
|
|
1734
2461
|
const err = [];
|
|
1735
2462
|
const code = await main([...args, "--json", "--root", ctx.root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
|
|
1736
|
-
const
|
|
2463
|
+
const text2 = out.join("\n").trim();
|
|
1737
2464
|
if (code === 2) {
|
|
1738
2465
|
const words = err.join("\n") || "The command refused the request.";
|
|
1739
2466
|
if (scanFirst(words))
|
|
@@ -1742,18 +2469,24 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1742
2469
|
}
|
|
1743
2470
|
let body;
|
|
1744
2471
|
try {
|
|
1745
|
-
body =
|
|
2472
|
+
body = text2 ? JSON.parse(text2) : null;
|
|
1746
2473
|
} catch {
|
|
1747
2474
|
body = void 0;
|
|
1748
2475
|
}
|
|
2476
|
+
if (body === null && code !== 0) {
|
|
2477
|
+
const words = err.join("\n").trim() || "The command gave no answer.";
|
|
2478
|
+
if (/^(There is no|No .+ (is|named) )/i.test(words))
|
|
2479
|
+
throw notFound(words);
|
|
2480
|
+
throw new HttpError(422, "Atticus can't show that yet", words, scanFirst(words) ? { notYet: true } : void 0);
|
|
2481
|
+
}
|
|
1749
2482
|
if (body === void 0) {
|
|
1750
2483
|
const words = [...err, ...out].join("\n") || "No output.";
|
|
1751
2484
|
throw new HttpError(422, "Atticus can't show that yet", words, scanFirst(words) ? { notYet: true } : void 0);
|
|
1752
2485
|
}
|
|
1753
2486
|
return { status: 200, body: { code, data: body, problems: err } };
|
|
1754
2487
|
};
|
|
1755
|
-
const
|
|
1756
|
-
const { main } = await import("./chunk-
|
|
2488
|
+
const text = async (args) => {
|
|
2489
|
+
const { main } = await import("./chunk-7FHNFCHK.js");
|
|
1757
2490
|
const out = [];
|
|
1758
2491
|
const err = [];
|
|
1759
2492
|
const code = await main([...args, "--root", ctx.root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
|
|
@@ -1771,7 +2504,7 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1771
2504
|
throw badRequest("Some fields aren't right.", {
|
|
1772
2505
|
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
1773
2506
|
});
|
|
1774
|
-
const { triageDecide } = await import("./chunk-
|
|
2507
|
+
const { triageDecide } = await import("./chunk-ZKYRDV7A.js");
|
|
1775
2508
|
const out = [];
|
|
1776
2509
|
const err = [];
|
|
1777
2510
|
const code = await triageDecide(ctx.root, params.itemId, b.data.option, { by: ctx.user, ...b.data.reason ? { reason: b.data.reason } : {} }, { out: (l) => out.push(l), err: (l) => err.push(l) });
|
|
@@ -1832,7 +2565,7 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1832
2565
|
throw badRequest(`${focus} isn't an id.`);
|
|
1833
2566
|
return {
|
|
1834
2567
|
status: 200,
|
|
1835
|
-
body: await
|
|
2568
|
+
body: await text(["diagram", "render", name, ...focus ? ["--focus", focus] : []])
|
|
1836
2569
|
};
|
|
1837
2570
|
}).add("GET", "/api/diagram/{view}/graph", async ({ params, query }) => {
|
|
1838
2571
|
const name = params.view;
|
|
@@ -1841,7 +2574,7 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1841
2574
|
const focus = query.get("focus") ?? void 0;
|
|
1842
2575
|
if (focus !== void 0 && !ID.test(focus))
|
|
1843
2576
|
throw badRequest(`${focus} isn't an id.`);
|
|
1844
|
-
const r2 = await
|
|
2577
|
+
const r2 = await text([
|
|
1845
2578
|
"diagram",
|
|
1846
2579
|
"render",
|
|
1847
2580
|
name,
|
|
@@ -1855,7 +2588,96 @@ function uiKnowRoutes(r, ctx, o = {}) {
|
|
|
1855
2588
|
throw new HttpError(422, "Atticus can't show that yet", words, scanFirst(words) ? { notYet: true } : void 0);
|
|
1856
2589
|
}
|
|
1857
2590
|
return { status: 200, body: { view: JSON.parse(out) } };
|
|
1858
|
-
}).add("GET", "/api/
|
|
2591
|
+
}).add("GET", "/api/coverage", async () => {
|
|
2592
|
+
const r2 = await view3("coverage");
|
|
2593
|
+
const data = r2.body.data;
|
|
2594
|
+
return { status: 200, body: data?.api ?? { areas: [], systems: [] } };
|
|
2595
|
+
}).add("GET", "/api/view/{name}", ({ params }) => view3(params.name)).add("GET", "/api/view/{name}/{id}", ({ params }) => view3(params.name, params.id));
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
// packages/cli/dist/ui-api-initiatives.js
|
|
2599
|
+
var ID2 = /^[a-z0-9][a-z0-9._-]*$/;
|
|
2600
|
+
async function run3(f) {
|
|
2601
|
+
const out = [];
|
|
2602
|
+
const err = [];
|
|
2603
|
+
const code = await f({ out: (l) => out.push(l), err: (l) => err.push(l) });
|
|
2604
|
+
return { code, out, err };
|
|
2605
|
+
}
|
|
2606
|
+
function parseBody3(schema, body) {
|
|
2607
|
+
const r = schema.safeParse(body ?? {});
|
|
2608
|
+
if (!r.success)
|
|
2609
|
+
throw badRequest("Some fields aren't right.", {
|
|
2610
|
+
errors: r.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
2611
|
+
});
|
|
2612
|
+
return r.data;
|
|
2613
|
+
}
|
|
2614
|
+
function refused3(r) {
|
|
2615
|
+
const detail = [...r.err, ...r.out].join("\n") || "Atticus refused.";
|
|
2616
|
+
if (/^There is no (initiative|commitment) /m.test(detail))
|
|
2617
|
+
return notFound(detail);
|
|
2618
|
+
if (/already exists/.test(detail))
|
|
2619
|
+
return conflict(detail);
|
|
2620
|
+
return r.code === 2 ? badRequest(detail) : new HttpError(422, "Atticus refused", detail);
|
|
2621
|
+
}
|
|
2622
|
+
function uiInitiativeRoutes(r, ctx) {
|
|
2623
|
+
const commands = () => import("./chunk-AE4P57KK.js");
|
|
2624
|
+
const changed = () => ctx.events.publish({ type: "file.changed", path: "catalog/curated" });
|
|
2625
|
+
return r.add("POST", "/api/initiatives", async ({ body }) => {
|
|
2626
|
+
const b = parseBody3(api_exports.NewInitiativeRequest, body);
|
|
2627
|
+
const { initiativeNew } = await commands();
|
|
2628
|
+
const res = await run3((io) => initiativeNew(ctx.root, {
|
|
2629
|
+
title: b.title,
|
|
2630
|
+
journey: b.journey,
|
|
2631
|
+
by: ctx.user,
|
|
2632
|
+
...b.id ? { id: b.id } : {},
|
|
2633
|
+
...b.release ? { release: b.release } : {},
|
|
2634
|
+
...b.epic ? { epic: b.epic } : {},
|
|
2635
|
+
json: true
|
|
2636
|
+
}, io));
|
|
2637
|
+
if (res.code !== 0)
|
|
2638
|
+
throw refused3(res);
|
|
2639
|
+
const made = JSON.parse(res.out.join("\n"));
|
|
2640
|
+
changed();
|
|
2641
|
+
return {
|
|
2642
|
+
status: 201,
|
|
2643
|
+
body: {
|
|
2644
|
+
initiative: made.initiative.id,
|
|
2645
|
+
lines: [
|
|
2646
|
+
`Started ${made.initiative.id} (${made.initiative.journey}). Wrote ${made.written}.`
|
|
2647
|
+
]
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
}).add("POST", "/api/initiatives/{initiativeId}/plan", async ({ params, body }) => {
|
|
2651
|
+
const id = params.initiativeId;
|
|
2652
|
+
if (!ID2.test(id))
|
|
2653
|
+
throw badRequest(`${id} isn't an id.`);
|
|
2654
|
+
const b = parseBody3(api_exports.InitiativePlanRequest, body);
|
|
2655
|
+
const { initiativePlan } = await commands();
|
|
2656
|
+
const res = await run3((io) => initiativePlan(ctx.root, id, {
|
|
2657
|
+
by: ctx.user,
|
|
2658
|
+
...b.target ? { target: b.target } : {},
|
|
2659
|
+
...b.release ? { release: b.release } : {},
|
|
2660
|
+
...b.systems?.length ? { systems: b.systems } : {}
|
|
2661
|
+
}, io));
|
|
2662
|
+
if (res.code !== 0)
|
|
2663
|
+
throw refused3(res);
|
|
2664
|
+
changed();
|
|
2665
|
+
return { status: 200, body: { initiative: id, lines: res.out } };
|
|
2666
|
+
}).add("POST", "/api/commitments/{commitmentId}/status", async ({ params, body }) => {
|
|
2667
|
+
const id = params.commitmentId;
|
|
2668
|
+
if (!ID2.test(id))
|
|
2669
|
+
throw badRequest(`${id} isn't an id.`);
|
|
2670
|
+
const b = parseBody3(api_exports.CommitmentStatusRequest, body);
|
|
2671
|
+
const c = (await readView(ctx.root)).get("Commitment", id);
|
|
2672
|
+
if (!c)
|
|
2673
|
+
throw notFound(`There is no commitment ${id}.`);
|
|
2674
|
+
const { initiativeCommitment } = await commands();
|
|
2675
|
+
const res = await run3((io) => initiativeCommitment(ctx.root, id, b.status, { by: ctx.user, ...b.note ? { note: b.note } : {} }, io));
|
|
2676
|
+
if (res.code !== 0)
|
|
2677
|
+
throw refused3(res);
|
|
2678
|
+
changed();
|
|
2679
|
+
return { status: 200, body: { initiative: c.initiative, lines: res.out } };
|
|
2680
|
+
});
|
|
1859
2681
|
}
|
|
1860
2682
|
|
|
1861
2683
|
// packages/cli/dist/ui-api-questions.js
|
|
@@ -1865,8 +2687,82 @@ function uiQuestionRoutes(r, ctx, deps = {}) {
|
|
|
1865
2687
|
const state = strParam(query, "state");
|
|
1866
2688
|
if (state !== void 0 && !QuestionState.safeParse(state).success)
|
|
1867
2689
|
throw badRequest("state is open, answered or retired.");
|
|
1868
|
-
const
|
|
2690
|
+
const mine = strParam(query, "mine");
|
|
2691
|
+
if (mine !== void 0 && mine !== "true")
|
|
2692
|
+
throw badRequest("mine is true, or left out.");
|
|
2693
|
+
const change = strParam(query, "change");
|
|
2694
|
+
if (change !== void 0 && !RecordId.safeParse(change).success)
|
|
2695
|
+
throw badRequest("change is a change's id, or left out.");
|
|
2696
|
+
const role = strParam(query, "role");
|
|
2697
|
+
if (role !== void 0 && !QuestionRole.safeParse(role).success)
|
|
2698
|
+
throw badRequest("role is product, architecture, analysis or delivery, or left out.");
|
|
2699
|
+
const onFile = await listQuestions(ctx.root);
|
|
2700
|
+
const every = { ...onFile, questions: onFile.questions.filter((q) => !q.finding) };
|
|
2701
|
+
const { routeQuestions } = await import("./chunk-Z3QFH7YG.js");
|
|
2702
|
+
const routedAll = await routeQuestions(ctx.root, every.questions, mine ? { mine: ctx.user } : {});
|
|
2703
|
+
const { stageQuestions, roleWithStage, isChangeQuestion, countByRole, personsRoles } = await import("./chunk-KYZMRSPS.js");
|
|
2704
|
+
const stages = await stageQuestions(ctx.root);
|
|
2705
|
+
const roleOf = (q) => roleWithStage(q, stages);
|
|
2706
|
+
for (const q of every.questions) {
|
|
2707
|
+
const r2 = roleOf(q);
|
|
2708
|
+
const a = routedAll.about[q.id];
|
|
2709
|
+
if (r2 && a)
|
|
2710
|
+
a.role = r2;
|
|
2711
|
+
}
|
|
2712
|
+
const base = change ? routedAll.questions.filter((q) => isChangeQuestion(q, change, { stages, restsOn: routedAll.about[q.id]?.restsOn })) : routedAll.questions;
|
|
2713
|
+
const routed = {
|
|
2714
|
+
...routedAll,
|
|
2715
|
+
questions: base.filter((q) => (!state || q.state === state) && (!role || roleOf(q) === role))
|
|
2716
|
+
};
|
|
2717
|
+
const listed = { problems: every.problems };
|
|
2718
|
+
const n = (s) => base.filter((q) => q.state === s).length;
|
|
2719
|
+
const body = {
|
|
2720
|
+
counts: {
|
|
2721
|
+
open: n("open"),
|
|
2722
|
+
answered: n("answered"),
|
|
2723
|
+
retired: n("retired"),
|
|
2724
|
+
total: base.length,
|
|
2725
|
+
byRepo: Object.fromEntries([...new Set(base.map((q) => q.repo))].sort().map((r2) => {
|
|
2726
|
+
const of = base.filter((q) => q.repo === r2);
|
|
2727
|
+
const k = (s) => of.filter((q) => q.state === s).length;
|
|
2728
|
+
return [
|
|
2729
|
+
r2,
|
|
2730
|
+
{
|
|
2731
|
+
open: k("open"),
|
|
2732
|
+
answered: k("answered"),
|
|
2733
|
+
retired: k("retired"),
|
|
2734
|
+
total: of.length
|
|
2735
|
+
}
|
|
2736
|
+
];
|
|
2737
|
+
})),
|
|
2738
|
+
byRole: countByRole(base, roleOf)
|
|
2739
|
+
},
|
|
2740
|
+
questions: routed.questions,
|
|
2741
|
+
problems: [...listed.problems, ...routed.problems],
|
|
2742
|
+
about: routed.about,
|
|
2743
|
+
...routed.me ? { me: routed.me } : {},
|
|
2744
|
+
...change ? { yourRoles: await personsRoles(ctx.root, change, ctx.user) } : {}
|
|
2745
|
+
};
|
|
1869
2746
|
return { status: 200, body };
|
|
2747
|
+
}).add("GET", "/api/owner-questions", async () => {
|
|
2748
|
+
const { ownerQuestions } = await import("./chunk-M4H2UG4R.js");
|
|
2749
|
+
return { status: 200, body: await ownerQuestions(ctx.root, { now: now() }) };
|
|
2750
|
+
}).add("POST", "/api/owner-questions/{id}/answer", async ({ params, body }) => {
|
|
2751
|
+
const b = api_exports.OwnerAnswerRequest.safeParse(body ?? {});
|
|
2752
|
+
if (!b.success || !b.data.answer.trim())
|
|
2753
|
+
throw badRequest('Answer with { answer: "what the owner said" }.');
|
|
2754
|
+
const { answerOwnerQuestion, OwnerQuestionError } = await import("./chunk-M4H2UG4R.js");
|
|
2755
|
+
try {
|
|
2756
|
+
const r2 = await answerOwnerQuestion(ctx.root, params.id, b.data.answer, {
|
|
2757
|
+
by: ctx.user,
|
|
2758
|
+
now: now()
|
|
2759
|
+
});
|
|
2760
|
+
return { status: 200, body: r2 };
|
|
2761
|
+
} catch (e) {
|
|
2762
|
+
if (e instanceof OwnerQuestionError)
|
|
2763
|
+
throw e.code === "not-found" ? notFound(e.message) : badRequest(e.message);
|
|
2764
|
+
throw e;
|
|
2765
|
+
}
|
|
1870
2766
|
}).add("GET", "/api/questions/{id}", async ({ params }) => {
|
|
1871
2767
|
const q = await readQuestion(ctx.root, params.id).catch(() => {
|
|
1872
2768
|
throw new HttpError(422, "Atticus can't read that question", `questions/${params.id}.yaml isn't a valid question.`);
|
|
@@ -1874,11 +2770,19 @@ function uiQuestionRoutes(r, ctx, deps = {}) {
|
|
|
1874
2770
|
if (!q)
|
|
1875
2771
|
throw notFound(`There is no question ${params.id}.`);
|
|
1876
2772
|
return { status: 200, body: q };
|
|
2773
|
+
}).add("GET", "/api/questions/{id}/targets", async ({ params }) => {
|
|
2774
|
+
const q = await readQuestion(ctx.root, params.id).catch(() => void 0);
|
|
2775
|
+
if (!q)
|
|
2776
|
+
throw notFound(`There is no question ${params.id}.`);
|
|
2777
|
+
if (q.askedBy.step !== TARGETS_STEP_ID && q.askedBy.step !== TOPICS_STEP_ID)
|
|
2778
|
+
throw new HttpError(422, "That question doesn't ask where a call goes or who owns a topic", `${q.id} isn't a targets or topics question, so it has no picker.`);
|
|
2779
|
+
const { pickerOf } = await import("./chunk-IFFRGC3N.js");
|
|
2780
|
+
return { status: 200, body: { picks: await pickerOf(ctx.root) } };
|
|
1877
2781
|
}).add("GET", "/api/questions/{id}/evidence", async ({ params }) => {
|
|
1878
2782
|
const q = await readQuestion(ctx.root, params.id).catch(() => void 0);
|
|
1879
2783
|
if (!q)
|
|
1880
2784
|
throw notFound(`There is no question ${params.id}.`);
|
|
1881
|
-
const { evidenceExcerpts } = await import("./chunk-
|
|
2785
|
+
const { evidenceExcerpts } = await import("./chunk-UFE36WJH.js");
|
|
1882
2786
|
return {
|
|
1883
2787
|
status: 200,
|
|
1884
2788
|
body: {
|
|
@@ -1892,8 +2796,39 @@ function uiQuestionRoutes(r, ctx, deps = {}) {
|
|
|
1892
2796
|
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
1893
2797
|
});
|
|
1894
2798
|
try {
|
|
1895
|
-
const
|
|
1896
|
-
|
|
2799
|
+
const { acceptProposal: acceptProposal2, target, addSystem, name, ...given } = b.data;
|
|
2800
|
+
let writeSystem;
|
|
2801
|
+
let system;
|
|
2802
|
+
if (target) {
|
|
2803
|
+
if (acceptProposal2 || given.option !== void 0 || given.text !== void 0)
|
|
2804
|
+
throw badRequest("Give target alone, not with option, text or acceptProposal.");
|
|
2805
|
+
const q0 = await readQuestion(ctx.root, params.id).catch(() => void 0);
|
|
2806
|
+
if (!q0)
|
|
2807
|
+
throw notFound(`There is no question ${params.id}.`);
|
|
2808
|
+
const { targetAnswerOf, offerLine } = await import("./chunk-IFFRGC3N.js");
|
|
2809
|
+
const t = await targetAnswerOf(ctx.root, q0, target, {
|
|
2810
|
+
...addSystem ? { addSystem } : {},
|
|
2811
|
+
...name ? { name } : {}
|
|
2812
|
+
});
|
|
2813
|
+
if ("problem" in t)
|
|
2814
|
+
throw badRequest([t.problem, ...t.offer ? [offerLine(q0.id, target, t.offer)] : []].join(" "), t.offer ? { offer: t.offer } : void 0);
|
|
2815
|
+
given.text = t.text;
|
|
2816
|
+
writeSystem = t.write;
|
|
2817
|
+
}
|
|
2818
|
+
const r2 = acceptProposal2 ? await acceptProposal(ctx.root, params.id, {
|
|
2819
|
+
...given.scope ? { scope: given.scope } : {},
|
|
2820
|
+
...given.repoType ? { repoType: given.repoType } : {},
|
|
2821
|
+
...given.match ? { match: given.match } : {},
|
|
2822
|
+
by: ctx.user,
|
|
2823
|
+
at: now().toISOString()
|
|
2824
|
+
}) : await answerQuestion(ctx.root, params.id, {
|
|
2825
|
+
...given,
|
|
2826
|
+
// Review item 14: the profile is written only once the answer is accepted.
|
|
2827
|
+
...writeSystem ? {
|
|
2828
|
+
beforeWrite: async () => {
|
|
2829
|
+
system = await writeSystem();
|
|
2830
|
+
}
|
|
2831
|
+
} : {},
|
|
1897
2832
|
by: ctx.user,
|
|
1898
2833
|
at: now().toISOString()
|
|
1899
2834
|
});
|
|
@@ -1902,7 +2837,8 @@ function uiQuestionRoutes(r, ctx, deps = {}) {
|
|
|
1902
2837
|
body: {
|
|
1903
2838
|
question: r2.question,
|
|
1904
2839
|
rerun: r2.question.rerun,
|
|
1905
|
-
...r2.skill ? { skill: r2.skill } : {}
|
|
2840
|
+
...r2.skill ? { skill: r2.skill } : {},
|
|
2841
|
+
...system ? { system } : {}
|
|
1906
2842
|
}
|
|
1907
2843
|
};
|
|
1908
2844
|
} catch (e) {
|
|
@@ -1940,15 +2876,15 @@ async function latestFindings3(root, change) {
|
|
|
1940
2876
|
}
|
|
1941
2877
|
var SEVERITY_ORDER = ["blocking", "gap", "risk", "idea"];
|
|
1942
2878
|
var AT_RELEASE = /* @__PURE__ */ new Set(["release-ready", "released", "post-release-review"]);
|
|
1943
|
-
async function acrossRows(root,
|
|
2879
|
+
async function acrossRows(root, view3) {
|
|
1944
2880
|
const changes = await openChanges3(root);
|
|
1945
2881
|
const rows = [];
|
|
1946
|
-
const op =
|
|
1947
|
-
const steps =
|
|
2882
|
+
const op = view3 === "requests" ? await loadOrgPack(orgPackDirOf(root)) : void 0;
|
|
2883
|
+
const steps = view3 === "requests" ? await fileStepStore(root).list({ system: "service-desk" }).catch(() => []) : [];
|
|
1948
2884
|
for (const c of changes) {
|
|
1949
2885
|
const base = { change: c.id, changeTitle: c.title, changeState: c.state };
|
|
1950
2886
|
const dir = changeFolder(root, c.id);
|
|
1951
|
-
switch (
|
|
2887
|
+
switch (view3) {
|
|
1952
2888
|
case "findings":
|
|
1953
2889
|
for (const f of await latestFindings3(root, c.id))
|
|
1954
2890
|
rows.push({
|
|
@@ -2035,9 +2971,9 @@ async function acrossRows(root, view2) {
|
|
|
2035
2971
|
}
|
|
2036
2972
|
}
|
|
2037
2973
|
}
|
|
2038
|
-
if (
|
|
2974
|
+
if (view3 === "findings")
|
|
2039
2975
|
rows.sort((a, b) => Number(a.state !== "open") - Number(b.state !== "open") || SEVERITY_ORDER.indexOf(String(a.severity)) - SEVERITY_ORDER.indexOf(String(b.severity)) || a.change.localeCompare(b.change));
|
|
2040
|
-
if (
|
|
2976
|
+
if (view3 === "releases")
|
|
2041
2977
|
rows.sort((a, b) => Number(b.atRelease) - Number(a.atRelease));
|
|
2042
2978
|
return rows;
|
|
2043
2979
|
}
|
|
@@ -2127,13 +3063,13 @@ async function hopsFromTargetFlows(dir, mappings, interfaces) {
|
|
|
2127
3063
|
function uiAcrossRoutes(r, ctx) {
|
|
2128
3064
|
const { root } = ctx;
|
|
2129
3065
|
return r.add("GET", "/api/across", async ({ query }) => {
|
|
2130
|
-
const
|
|
2131
|
-
if (!ACROSS_VIEWS.includes(
|
|
3066
|
+
const view3 = strParam(query, "view") ?? "";
|
|
3067
|
+
if (!ACROSS_VIEWS.includes(view3))
|
|
2132
3068
|
throw badRequest(`Say which view: ?view= one of ${ACROSS_VIEWS.join(", ")}.`);
|
|
2133
|
-
const rows = await acrossRows(root,
|
|
3069
|
+
const rows = await acrossRows(root, view3);
|
|
2134
3070
|
return {
|
|
2135
3071
|
status: 200,
|
|
2136
|
-
body: { view:
|
|
3072
|
+
body: { view: view3, changes: (await openChanges3(root)).length, rows }
|
|
2137
3073
|
};
|
|
2138
3074
|
}).add("GET", "/api/changes/{changeId}/mappings", async ({ params }) => {
|
|
2139
3075
|
try {
|
|
@@ -2146,8 +3082,9 @@ function uiAcrossRoutes(r, ctx) {
|
|
|
2146
3082
|
}
|
|
2147
3083
|
|
|
2148
3084
|
// packages/cli/dist/ui-api-graph.js
|
|
2149
|
-
var
|
|
2150
|
-
import { readFile as readFile3 } from "node:fs/promises";
|
|
3085
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
3086
|
+
import { readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
3087
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
2151
3088
|
import { join as join4 } from "node:path";
|
|
2152
3089
|
var lensOf = (v, fallback) => {
|
|
2153
3090
|
if (v === null)
|
|
@@ -2167,13 +3104,27 @@ var keyOf = (k) => {
|
|
|
2167
3104
|
throw badRequest(`${k} isn't a view key. Use <lens>.<node>, e.g. architecture.estate.`);
|
|
2168
3105
|
return k;
|
|
2169
3106
|
};
|
|
3107
|
+
function vscodeFileLink(path, line) {
|
|
3108
|
+
const posix = path.replace(/\\/g, "/");
|
|
3109
|
+
const segs = (posix.startsWith("/") ? posix.slice(1) : posix).split("/").map((x) => /^[A-Za-z]:$/.test(x) ? x : encodeURIComponent(x));
|
|
3110
|
+
return `vscode://file/${segs.join("/")}:${line}`;
|
|
3111
|
+
}
|
|
2170
3112
|
function uiGraphRoutes(r, ctx) {
|
|
2171
|
-
const
|
|
3113
|
+
const memoKey = `estate:${ctx.root}`;
|
|
3114
|
+
ctx.events.listen((e) => {
|
|
3115
|
+
if (e.type === "file.changed" && typeof e.path === "string" && /^(\.atticus[\\/])?catalog[\\/]/.test(e.path))
|
|
3116
|
+
recheckNow(memoKey);
|
|
3117
|
+
});
|
|
3118
|
+
const estate = async () => memoByFiles(memoKey, () => filesUnder(catalogDirOf(ctx.root)), buildFromCatalog, {
|
|
3119
|
+
recheckAfterMs: 1e3
|
|
3120
|
+
});
|
|
3121
|
+
const buildFromCatalog = async () => {
|
|
2172
3122
|
const records = await catalogRecords(ctx.root);
|
|
2173
3123
|
if (!records.length)
|
|
2174
3124
|
throw new HttpError(422, "Atticus can't show that yet", "The catalog is empty: read the repos first (atticus map <repo>, then atticus verify <repo> --write).", { notYet: true });
|
|
2175
3125
|
return buildEstate(records);
|
|
2176
3126
|
};
|
|
3127
|
+
setTimeout(() => void estate().catch(() => void 0), 0).unref?.();
|
|
2177
3128
|
const guard = (f) => {
|
|
2178
3129
|
try {
|
|
2179
3130
|
return f();
|
|
@@ -2183,11 +3134,13 @@ function uiGraphRoutes(r, ctx) {
|
|
|
2183
3134
|
throw e;
|
|
2184
3135
|
}
|
|
2185
3136
|
};
|
|
2186
|
-
const
|
|
3137
|
+
const layoutRel = (view3) => isAdopted(ctx.root) ? `.atticus/${layoutPath(view3)}` : layoutPath(view3);
|
|
3138
|
+
const viewsRel = diagramsRelOf(ctx.root) + "/views";
|
|
3139
|
+
const readLayout = async (view3) => {
|
|
2187
3140
|
try {
|
|
2188
|
-
const f = DiagramLayout.safeParse((0,
|
|
3141
|
+
const f = DiagramLayout.safeParse((0, import_yaml3.parse)(await readFile3(join4(ctx.root, layoutRel(view3)), "utf8")));
|
|
2189
3142
|
if (!f.success)
|
|
2190
|
-
throw new HttpError(422, "Atticus refused", `${
|
|
3143
|
+
throw new HttpError(422, "Atticus refused", `${layoutRel(view3)} isn't a layout file.`);
|
|
2191
3144
|
return f.data;
|
|
2192
3145
|
} catch (e) {
|
|
2193
3146
|
if (e.code === "ENOENT")
|
|
@@ -2211,46 +3164,100 @@ function uiGraphRoutes(r, ctx) {
|
|
|
2211
3164
|
const lens = lensOf(query.get("lens"), "data");
|
|
2212
3165
|
const est = await estate();
|
|
2213
3166
|
return { status: 200, body: guard(() => focusView(est, id, { lens })) };
|
|
3167
|
+
}).add("GET", "/api/graph/views", async () => {
|
|
3168
|
+
const dir = join4(ctx.root, ...viewsRel.split("/"));
|
|
3169
|
+
const names = (await readdir3(dir).catch(() => [])).filter((n) => n.endsWith(".yaml")).sort();
|
|
3170
|
+
const views = [];
|
|
3171
|
+
for (const n of names) {
|
|
3172
|
+
const v = api_exports.SavedCanvasView.safeParse((0, import_yaml3.parse)(await readFile3(join4(dir, n), "utf8").catch(() => "")));
|
|
3173
|
+
if (v.success)
|
|
3174
|
+
views.push(v.data);
|
|
3175
|
+
}
|
|
3176
|
+
return {
|
|
3177
|
+
status: 200,
|
|
3178
|
+
body: { views, dir: viewsRel }
|
|
3179
|
+
};
|
|
3180
|
+
}).add("PUT", "/api/graph/views/{name}", async ({ params, body, query }) => {
|
|
3181
|
+
const b = api_exports.SavedCanvasView.safeParse({ ...body, name: params.name });
|
|
3182
|
+
if (!b.success)
|
|
3183
|
+
throw badRequest("Name the view (lower case, digits and dashes) and give its lens and node.", {
|
|
3184
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
3185
|
+
});
|
|
3186
|
+
const v = { ...b.data, by: ctx.user, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3187
|
+
const rel = `${viewsRel}/${v.name}.yaml`;
|
|
3188
|
+
if (existsSync2(join4(ctx.root, rel)) && query.get("replace") !== "1")
|
|
3189
|
+
throw conflict(`There is a view ${v.name} already (${rel}): pick another name, or replace it.`);
|
|
3190
|
+
await writeFileAtomic(join4(ctx.root, rel), (0, import_yaml3.stringify)(v));
|
|
3191
|
+
ctx.events.publish({ type: "file.changed", path: rel });
|
|
3192
|
+
return { status: 200, body: v };
|
|
3193
|
+
}).add("GET", "/api/code-link", async ({ query }) => {
|
|
3194
|
+
const repo = query.get("repo") ?? "";
|
|
3195
|
+
const at = query.get("at") ?? "";
|
|
3196
|
+
const m = /^(.+):(\d+)$/.exec(at);
|
|
3197
|
+
if (!/^[A-Za-z0-9._-]{1,200}$/.test(repo) || !m || m[1].includes(".."))
|
|
3198
|
+
throw badRequest("Say repo=<repo> and at=<file>:<line>.");
|
|
3199
|
+
const line = Number(m[2]);
|
|
3200
|
+
if (line < 1)
|
|
3201
|
+
throw badRequest("A line is 1 or more.");
|
|
3202
|
+
const dir = await checkoutOf(ctx.root, repo).catch(() => void 0);
|
|
3203
|
+
if (!dir || !existsSync2(dir))
|
|
3204
|
+
return {
|
|
3205
|
+
status: 200,
|
|
3206
|
+
body: {
|
|
3207
|
+
line,
|
|
3208
|
+
missing: `There is no checkout of ${repo} here (atticus sync clones it into the workspace).`
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
const path = join4(dir, ...m[1].split("/"));
|
|
3212
|
+
return {
|
|
3213
|
+
status: 200,
|
|
3214
|
+
body: {
|
|
3215
|
+
path,
|
|
3216
|
+
line,
|
|
3217
|
+
vscode: vscodeFileLink(path, line),
|
|
3218
|
+
idea: `idea://open?file=${encodeURIComponent(path)}&line=${line}`
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
2214
3221
|
}).add("GET", "/api/graph/layout/{viewKey}", async ({ params }) => {
|
|
2215
|
-
const
|
|
2216
|
-
const f = await readLayout(
|
|
3222
|
+
const view3 = keyOf(params.viewKey);
|
|
3223
|
+
const f = await readLayout(view3);
|
|
2217
3224
|
return {
|
|
2218
3225
|
status: 200,
|
|
2219
|
-
body: { view:
|
|
3226
|
+
body: { view: view3, saved: !!f, path: layoutRel(view3), positions: f?.positions ?? {} }
|
|
2220
3227
|
};
|
|
2221
3228
|
}).add("PUT", "/api/graph/layout/{viewKey}", async ({ params, body }) => {
|
|
2222
|
-
const
|
|
3229
|
+
const view3 = keyOf(params.viewKey);
|
|
2223
3230
|
const b = api_exports.PutLayoutRequest.safeParse(body ?? {});
|
|
2224
3231
|
if (!b.success)
|
|
2225
3232
|
throw badRequest("Some fields aren't right.", {
|
|
2226
3233
|
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
2227
3234
|
});
|
|
2228
3235
|
const { lens, node, positions } = b.data;
|
|
2229
|
-
if (
|
|
2230
|
-
throw badRequest(`${
|
|
3236
|
+
if (view3 !== viewKey(lens, node) && view3 !== viewKey(lens, node, "focus"))
|
|
3237
|
+
throw badRequest(`${view3} isn't the view key of ${node} in the ${lens} lens.`);
|
|
2231
3238
|
if (Object.keys(positions).length > 5e3)
|
|
2232
3239
|
throw badRequest("At most 5000 boxes per view.");
|
|
2233
3240
|
const sorted = Object.fromEntries(Object.entries(positions).sort(([a], [b2]) => a < b2 ? -1 : 1).map(([k, p]) => [k, { x: Math.round(p.x), y: Math.round(p.y) }]));
|
|
2234
3241
|
const file = DiagramLayout.parse({
|
|
2235
3242
|
schemaVersion: LAYOUT_SCHEMA_VERSION,
|
|
2236
|
-
view:
|
|
3243
|
+
view: view3,
|
|
2237
3244
|
lens,
|
|
2238
3245
|
node,
|
|
2239
3246
|
by: ctx.user,
|
|
2240
3247
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2241
3248
|
positions: sorted
|
|
2242
3249
|
});
|
|
2243
|
-
await writeFileAtomic(join4(ctx.root,
|
|
2244
|
-
ctx.events.publish({ type: "file.changed", path:
|
|
3250
|
+
await writeFileAtomic(join4(ctx.root, layoutRel(view3)), (0, import_yaml3.stringify)(file));
|
|
3251
|
+
ctx.events.publish({ type: "file.changed", path: layoutRel(view3) });
|
|
2245
3252
|
return {
|
|
2246
3253
|
status: 200,
|
|
2247
|
-
body: { view:
|
|
3254
|
+
body: { view: view3, saved: true, path: layoutRel(view3), positions: file.positions }
|
|
2248
3255
|
};
|
|
2249
3256
|
});
|
|
2250
3257
|
}
|
|
2251
3258
|
|
|
2252
3259
|
// packages/cli/dist/ui-api-specs.js
|
|
2253
|
-
import { stat as
|
|
3260
|
+
import { stat as stat3 } from "node:fs/promises";
|
|
2254
3261
|
import { join as join5 } from "node:path";
|
|
2255
3262
|
var idOf2 = (v, what) => {
|
|
2256
3263
|
if (!RecordId.safeParse(v).success)
|
|
@@ -2357,7 +3364,7 @@ function uiSpecRoutes(r, ctx) {
|
|
|
2357
3364
|
});
|
|
2358
3365
|
const deltasOf = async (change) => {
|
|
2359
3366
|
const dir = join5(changesDirOf(root), change);
|
|
2360
|
-
if (!await
|
|
3367
|
+
if (!await stat3(join5(dir, "change.yaml")).catch(() => void 0))
|
|
2361
3368
|
throw notFound(`There is no change ${change}.`);
|
|
2362
3369
|
return readDeltas(dir);
|
|
2363
3370
|
};
|
|
@@ -2381,7 +3388,7 @@ function uiSpecRoutes(r, ctx) {
|
|
|
2381
3388
|
});
|
|
2382
3389
|
r.add("GET", "/api/changes/{changeId}/conformance", async ({ params }) => {
|
|
2383
3390
|
const change = idOf2(params.changeId, "change");
|
|
2384
|
-
const { conformanceOf } = await import("./chunk-
|
|
3391
|
+
const { conformanceOf } = await import("./chunk-FRHD22WM.js");
|
|
2385
3392
|
const c = await conformanceOf(root, change);
|
|
2386
3393
|
if (!c)
|
|
2387
3394
|
throw notFound(`There is no change ${change}.`);
|
|
@@ -2394,6 +3401,14 @@ function uiSpecRoutes(r, ctx) {
|
|
|
2394
3401
|
throw notFound(`There is no change ${change}.`);
|
|
2395
3402
|
return { status: 200, body: e };
|
|
2396
3403
|
});
|
|
3404
|
+
r.add("GET", "/api/changes/{changeId}/impact", async ({ params }) => {
|
|
3405
|
+
const change = idOf2(params.changeId, "change");
|
|
3406
|
+
const { changeImpactOf } = await import("./chunk-2D7PWSOK.js");
|
|
3407
|
+
const i = await changeImpactOf(root, change);
|
|
3408
|
+
if (!i)
|
|
3409
|
+
throw notFound(`There is no change ${change}.`);
|
|
3410
|
+
return { status: 200, body: i };
|
|
3411
|
+
});
|
|
2397
3412
|
r.add("GET", "/api/changes/{changeId}/map", async ({ params }) => {
|
|
2398
3413
|
const change = idOf2(params.changeId, "change");
|
|
2399
3414
|
const m = await changeMapOf(root, change);
|
|
@@ -2422,8 +3437,8 @@ function uiSpecRoutes(r, ctx) {
|
|
|
2422
3437
|
}
|
|
2423
3438
|
|
|
2424
3439
|
// packages/cli/dist/ui-api-knowledge.js
|
|
2425
|
-
var
|
|
2426
|
-
import { readFile as readFile4, stat as
|
|
3440
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
3441
|
+
import { readFile as readFile4, stat as stat4 } from "node:fs/promises";
|
|
2427
3442
|
import { join as join6 } from "node:path";
|
|
2428
3443
|
var STALE_DAYS = 14;
|
|
2429
3444
|
var SLUG = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -2493,7 +3508,7 @@ function candidateView(c) {
|
|
|
2493
3508
|
keepAs: keepAs(r.kind)
|
|
2494
3509
|
};
|
|
2495
3510
|
}
|
|
2496
|
-
var exists = (p) =>
|
|
3511
|
+
var exists = (p) => stat4(p).then(() => true, () => false);
|
|
2497
3512
|
function capture() {
|
|
2498
3513
|
const lines = [];
|
|
2499
3514
|
const problems = [];
|
|
@@ -2583,8 +3598,8 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2583
3598
|
const op = await loadOrgPack(orgPackDirOf(root));
|
|
2584
3599
|
const platforms = [];
|
|
2585
3600
|
for (const { platform } of op.profiles) {
|
|
2586
|
-
const
|
|
2587
|
-
const data =
|
|
3601
|
+
const text = await readFile4(join6(proposalsDirOf(root), "orgpack", `${platform}.yaml`), "utf8").catch(() => void 0);
|
|
3602
|
+
const data = text === void 0 ? void 0 : (0, import_yaml4.parse)(text);
|
|
2588
3603
|
const proposals = Array.isArray(data?.proposals) ? data.proposals.flatMap((p) => {
|
|
2589
3604
|
const x = OrgPackProposal.safeParse(p);
|
|
2590
3605
|
return x.success ? [x.data] : [];
|
|
@@ -2592,7 +3607,7 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2592
3607
|
platforms.push({
|
|
2593
3608
|
platform,
|
|
2594
3609
|
file: `${proposalsRelOf(root)}/orgpack/${platform}.yaml`,
|
|
2595
|
-
present:
|
|
3610
|
+
present: text !== void 0,
|
|
2596
3611
|
...typeof data?.at === "string" ? { at: data.at } : {},
|
|
2597
3612
|
proposals,
|
|
2598
3613
|
command: `atticus orgpack draft --platform ${platform}`
|
|
@@ -2615,8 +3630,8 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2615
3630
|
let snapshot;
|
|
2616
3631
|
if (date) {
|
|
2617
3632
|
const file = join6(knowledgeDirOf(root), "snapshots", w.id, date, SNAPSHOT_FILE);
|
|
2618
|
-
const
|
|
2619
|
-
const m =
|
|
3633
|
+
const text = await readFile4(file, "utf8").catch(() => void 0);
|
|
3634
|
+
const m = text === void 0 ? void 0 : SnapshotManifest.safeParse((0, import_yaml4.parse)(text));
|
|
2620
3635
|
if (m?.success)
|
|
2621
3636
|
snapshot = {
|
|
2622
3637
|
date,
|
|
@@ -2856,7 +3871,7 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2856
3871
|
if (fileExists && !f)
|
|
2857
3872
|
throw new HttpError(422, "Atticus can't read that file", `${changesRelOf(root)}/${change}/${NEEDS_FILE} isn't a valid needs file.`);
|
|
2858
3873
|
const k = f ? await readKnowledge(root, { cacheRoot: root }) : void 0;
|
|
2859
|
-
const
|
|
3874
|
+
const view3 = (n) => ({
|
|
2860
3875
|
...n,
|
|
2861
3876
|
...k?.reviews[n.item] ? { review: k.reviews[n.item] } : {}
|
|
2862
3877
|
});
|
|
@@ -2865,7 +3880,7 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2865
3880
|
file: `${changesRelOf(root)}/${change}/${NEEDS_FILE}`,
|
|
2866
3881
|
present: !!f,
|
|
2867
3882
|
...f ? { updatedAt: f.updatedAt } : {},
|
|
2868
|
-
needs: (f?.needs ?? []).map(
|
|
3883
|
+
needs: (f?.needs ?? []).map(view3),
|
|
2869
3884
|
considered: f?.considered ?? [],
|
|
2870
3885
|
command: `atticus change needs ${change}`,
|
|
2871
3886
|
slash: `/atticus-needs ${change}`
|
|
@@ -2930,7 +3945,8 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2930
3945
|
const result = {
|
|
2931
3946
|
knowledge: await appliesView(change),
|
|
2932
3947
|
file: before.file,
|
|
2933
|
-
commit: `git add ${before.file}
|
|
3948
|
+
commit: `git add ${before.file}
|
|
3949
|
+
git commit -m "${change}: the knowledge that applies"`
|
|
2934
3950
|
};
|
|
2935
3951
|
return { status: 200, body: result };
|
|
2936
3952
|
}).add("GET", "/api/orgpack/draft", async () => ({ status: 200, body: await draftView() })).add("POST", "/api/orgpack/draft", async ({ body }) => {
|
|
@@ -2968,17 +3984,17 @@ function uiKnowledgeRoutes(r, ctx, deps = {}) {
|
|
|
2968
3984
|
// packages/cli/dist/ui-api-runs.js
|
|
2969
3985
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2970
3986
|
import { join as join7 } from "node:path";
|
|
2971
|
-
var
|
|
3987
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
2972
3988
|
async function serverModel(root) {
|
|
2973
3989
|
return (await configuredModel(root))?.type ?? null;
|
|
2974
3990
|
}
|
|
2975
3991
|
async function configuredModel(root) {
|
|
2976
|
-
const
|
|
2977
|
-
if (!
|
|
3992
|
+
const text = await readFile5(join7(orgPackDirOf(root), "runtime.yaml"), "utf8").catch(() => void 0);
|
|
3993
|
+
if (!text)
|
|
2978
3994
|
return null;
|
|
2979
3995
|
let y;
|
|
2980
3996
|
try {
|
|
2981
|
-
y = (0,
|
|
3997
|
+
y = (0, import_yaml5.parse)(text);
|
|
2982
3998
|
} catch {
|
|
2983
3999
|
return null;
|
|
2984
4000
|
}
|
|
@@ -2994,26 +4010,29 @@ async function configuredModel(root) {
|
|
|
2994
4010
|
function uiModelRunRoutes(r, ctx, deps = {}) {
|
|
2995
4011
|
const root = ctx.root;
|
|
2996
4012
|
const runStep = deps.runStep ?? (async (args) => {
|
|
2997
|
-
const { main } = await import("./chunk-
|
|
4013
|
+
const { main } = await import("./chunk-7FHNFCHK.js");
|
|
2998
4014
|
const out = [];
|
|
2999
4015
|
const err = [];
|
|
3000
4016
|
const code = await main([...args, "--root", root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
|
|
3001
4017
|
return { code, out, err };
|
|
3002
4018
|
});
|
|
3003
|
-
return r.add("POST", "/api/runs", async ({ body }) => {
|
|
3004
|
-
const
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
if (!
|
|
3009
|
-
throw
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
4019
|
+
return r.add("POST", "/api/runs/handoff", async ({ body }) => {
|
|
4020
|
+
const words = stepWords(body);
|
|
4021
|
+
const { prepareStep } = await import("./chunk-J2O4LFVI.js");
|
|
4022
|
+
const { code, prepared } = await prepareStep(root, runStep, words);
|
|
4023
|
+
ctx.events.publish({ type: "file.changed", path: runsRelOf(root) });
|
|
4024
|
+
if (!prepared.runId && code !== 0)
|
|
4025
|
+
throw new HttpError(422, "The step wasn't prepared", prepared.lines.join("\n") || "The step prepared no run.");
|
|
4026
|
+
return { status: 201, body: prepared };
|
|
4027
|
+
}).add("POST", "/api/runs", async ({ body }) => {
|
|
4028
|
+
const words = stepWords(body);
|
|
3013
4029
|
const configured = await configuredModel(root);
|
|
3014
4030
|
const model = configured?.type;
|
|
3015
4031
|
if (!model)
|
|
3016
4032
|
throw conflict("This server's runtime.yaml configures no model, so it can't run an AI step. Run it in your assistant (its slash command), or from VS Code with Copilot.");
|
|
4033
|
+
const busy = ctx.jobs.runningOf("step");
|
|
4034
|
+
if (busy)
|
|
4035
|
+
throw conflict(`An AI step is running on this server (${busy.id}): wait for it to finish, or cancel it.`);
|
|
3017
4036
|
const job = ctx.jobs.start("step", async (c) => {
|
|
3018
4037
|
c.progress(0, 1, `Running atticus ${words.join(" ")} with ${model}`);
|
|
3019
4038
|
const before = new Set((await listRuns(root, { limit: 1e5 })).map((x) => x.runId));
|
|
@@ -3054,136 +4073,1054 @@ function uiModelRunRoutes(r, ctx, deps = {}) {
|
|
|
3054
4073
|
return { status: 200, body: t };
|
|
3055
4074
|
});
|
|
3056
4075
|
}
|
|
4076
|
+
function stepWords(body) {
|
|
4077
|
+
const args = body?.args;
|
|
4078
|
+
if (!Array.isArray(args) || args.length < 2 || !args.every((a) => typeof a === "string" && a.trim() !== ""))
|
|
4079
|
+
throw badRequest(`args is the step's command words and arguments, for example ["scan", "survey", "forge-ledger"].`);
|
|
4080
|
+
const words = args;
|
|
4081
|
+
if (!isHandoffStep(words[0], words[1]))
|
|
4082
|
+
throw badRequest(`atticus ${words.slice(0, 2).join(" ")} isn't an AI step; the app starts only those.`);
|
|
4083
|
+
const option = withoutSource(words).find((a) => a.startsWith("-"));
|
|
4084
|
+
if (option)
|
|
4085
|
+
throw badRequest(`${option} would be read as an option; give the step's arguments only.`);
|
|
4086
|
+
return words;
|
|
4087
|
+
}
|
|
4088
|
+
var SOURCE_STEPS = /* @__PURE__ */ new Set(["knowledge scan", "knowledge find"]);
|
|
4089
|
+
function withoutSource(words) {
|
|
4090
|
+
if (!SOURCE_STEPS.has(`${words[0]} ${words[1]}`))
|
|
4091
|
+
return words;
|
|
4092
|
+
const at = words.indexOf("--source");
|
|
4093
|
+
const value = words[at + 1];
|
|
4094
|
+
if (at < 0 || !value || value.startsWith("-"))
|
|
4095
|
+
return words;
|
|
4096
|
+
return [...words.slice(0, at), ...words.slice(at + 2)];
|
|
4097
|
+
}
|
|
3057
4098
|
|
|
3058
|
-
// packages/cli/dist/ui-api-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
4099
|
+
// packages/cli/dist/ui-api-copilot-runs.js
|
|
4100
|
+
import { randomBytes } from "node:crypto";
|
|
4101
|
+
|
|
4102
|
+
// packages/cli/dist/copilot-run.js
|
|
4103
|
+
var TAB_LINES = 5e3;
|
|
4104
|
+
var CopilotRun = class {
|
|
4105
|
+
view;
|
|
4106
|
+
current = "run";
|
|
4107
|
+
constructor(o) {
|
|
4108
|
+
this.view = {
|
|
4109
|
+
runId: o.runId,
|
|
4110
|
+
jobId: o.jobId,
|
|
4111
|
+
kind: o.kind,
|
|
4112
|
+
...o.repos?.length ? { repos: o.repos } : {},
|
|
4113
|
+
...o.force ? { force: true } : {},
|
|
4114
|
+
state: "running",
|
|
4115
|
+
...o.by ? { by: o.by } : {},
|
|
4116
|
+
...o.model ? { model: o.model } : {},
|
|
4117
|
+
startedAt: o.startedAt,
|
|
4118
|
+
progress: { repos: 0, repoIndex: 0, sessions: 0 },
|
|
4119
|
+
totals: { kept: 0, rejected: 0, tokens: 0 },
|
|
4120
|
+
tabs: [{ id: "run", title: "Run", state: "running", from: 0, lines: [] }]
|
|
3069
4121
|
};
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
4122
|
+
}
|
|
4123
|
+
tab(id) {
|
|
4124
|
+
return this.view.tabs.find((t) => t.id === id) ?? this.view.tabs[0];
|
|
4125
|
+
}
|
|
4126
|
+
push(id, lines) {
|
|
4127
|
+
const t = this.tab(id);
|
|
4128
|
+
const at = t.from + t.lines.length;
|
|
4129
|
+
t.lines.push(...lines);
|
|
4130
|
+
if (t.lines.length > TAB_LINES) {
|
|
4131
|
+
const drop = t.lines.length - TAB_LINES;
|
|
4132
|
+
t.lines.splice(0, drop);
|
|
4133
|
+
t.from += drop;
|
|
4134
|
+
}
|
|
4135
|
+
return { tab: t.id, title: t.title, at, lines };
|
|
4136
|
+
}
|
|
4137
|
+
/** A line the run printed: into the session under way (or just ended, until the next repo). */
|
|
4138
|
+
line(text) {
|
|
4139
|
+
return this.push(this.current, [text]);
|
|
4140
|
+
}
|
|
4141
|
+
/** A step of the run: progress, and the tabs it opens or closes. Returns the lines it adds. */
|
|
4142
|
+
event(e) {
|
|
4143
|
+
const v = this.view;
|
|
4144
|
+
const out = [];
|
|
4145
|
+
switch (e.type) {
|
|
4146
|
+
case "plan":
|
|
4147
|
+
v.progress.repos = e.repos.length;
|
|
4148
|
+
break;
|
|
4149
|
+
case "repo":
|
|
4150
|
+
v.progress.repoIndex = e.index;
|
|
4151
|
+
v.progress.repos = e.total;
|
|
4152
|
+
v.progress.repo = e.repo;
|
|
4153
|
+
delete v.progress.session;
|
|
4154
|
+
this.current = "run";
|
|
4155
|
+
break;
|
|
4156
|
+
case "session": {
|
|
4157
|
+
const id = `s${v.progress.sessions + 1}`;
|
|
4158
|
+
v.progress.sessions++;
|
|
4159
|
+
const title = `${e.repo}, ${e.label}${e.attempt ? " (again)" : ""}`;
|
|
4160
|
+
v.progress.session = title;
|
|
4161
|
+
v.tabs.push({ id, title, repo: e.repo, state: "running", from: 0, lines: [] });
|
|
4162
|
+
out.push(this.push("run", [`Session ${v.progress.sessions}: ${title}`]));
|
|
4163
|
+
this.current = id;
|
|
4164
|
+
break;
|
|
4165
|
+
}
|
|
4166
|
+
case "session.end": {
|
|
4167
|
+
const t = v.tabs.findLast((x) => x.repo === e.repo && x.state === "running");
|
|
4168
|
+
if (t) {
|
|
4169
|
+
t.state = e.failed ? "failed" : "ended";
|
|
4170
|
+
t.ended = e.failed ?? e.ended;
|
|
4171
|
+
out.push(this.push("run", [`${t.title}: ended (${t.ended})`]));
|
|
4172
|
+
}
|
|
4173
|
+
if (e.tokens)
|
|
4174
|
+
v.totals.tokens += e.tokens;
|
|
4175
|
+
delete v.progress.session;
|
|
4176
|
+
break;
|
|
4177
|
+
}
|
|
4178
|
+
case "verify":
|
|
4179
|
+
v.totals.kept += e.kept;
|
|
4180
|
+
v.totals.rejected += e.rejected;
|
|
4181
|
+
if (this.current !== "run")
|
|
4182
|
+
out.push(this.push("run", [
|
|
4183
|
+
`${e.repo}: ${e.kept} kept, ${e.rejected} rejected, coverage ${Math.round(e.coverage * 100)}%, ${e.open} open`
|
|
4184
|
+
]));
|
|
4185
|
+
break;
|
|
4186
|
+
}
|
|
4187
|
+
return out;
|
|
4188
|
+
}
|
|
4189
|
+
/** How it ended: done, stopped by a person, or failed with the error said. */
|
|
4190
|
+
finish(state, at, message) {
|
|
4191
|
+
const v = this.view;
|
|
4192
|
+
v.state = state;
|
|
4193
|
+
v.finishedAt = at;
|
|
4194
|
+
if (message)
|
|
4195
|
+
v.message = message;
|
|
4196
|
+
for (const t of v.tabs)
|
|
4197
|
+
if (t.state === "running") {
|
|
4198
|
+
t.state = state === "failed" ? "failed" : "ended";
|
|
4199
|
+
if (t.id !== "run")
|
|
4200
|
+
t.ended ??= state;
|
|
4201
|
+
}
|
|
4202
|
+
this.current = "run";
|
|
4203
|
+
return this.push("run", [
|
|
4204
|
+
state === "done" ? "The run finished." : state === "stopped" ? "Stopped. The next run carries on from here." : `The run stopped: ${message ?? "an error"}`
|
|
4205
|
+
]);
|
|
4206
|
+
}
|
|
4207
|
+
/** The state without the lines, for the live event. */
|
|
4208
|
+
summary() {
|
|
4209
|
+
const { tabs: _tabs, ...rest } = this.view;
|
|
4210
|
+
return structuredClone(rest);
|
|
4211
|
+
}
|
|
4212
|
+
};
|
|
4213
|
+
|
|
4214
|
+
// packages/cli/dist/ui-api-copilot-runs.js
|
|
4215
|
+
var defaultStarter = (root) => async (o, io, deps) => {
|
|
4216
|
+
const { mapAllCommand } = await import("./chunk-YF5DZCPN.js");
|
|
4217
|
+
return mapAllCommand(root, o, io, {
|
|
4218
|
+
...deps,
|
|
4219
|
+
makeAcp: async () => (await import("./chunk-ZPDDEVQX.js")).acpStepRunner(root)
|
|
3085
4220
|
});
|
|
4221
|
+
};
|
|
4222
|
+
var TAIL = 200;
|
|
4223
|
+
var NOT_ALLOWED = "runtime.yaml doesn't allow copilot-acp here (schemaVersion 3, copilot-acp in modes.allowed and copilot in assistants.allowed), so the app can't start Copilot itself. Read through Copilot Chat instead: atticus map --all in a terminal gives each part to paste.";
|
|
4224
|
+
async function allowedHere(root) {
|
|
4225
|
+
try {
|
|
4226
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
4227
|
+
const c = copilotAcpOf(op.runtime);
|
|
4228
|
+
return { allowed: copilotAcpAllowed(op), ...c.model ? { model: c.model } : {} };
|
|
4229
|
+
} catch {
|
|
4230
|
+
return { allowed: false };
|
|
4231
|
+
}
|
|
3086
4232
|
}
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
const
|
|
3098
|
-
const
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
4233
|
+
var optionsOf = (b) => ({
|
|
4234
|
+
mode: "copilot-acp",
|
|
4235
|
+
...b.kind === "stale" ? { stale: true } : {},
|
|
4236
|
+
...b.kind === "gaps" ? { gaps: true } : {},
|
|
4237
|
+
...b.repos?.length ? { repos: b.repos } : {},
|
|
4238
|
+
...b.force ? { force: true } : {},
|
|
4239
|
+
resumeAs: "the next run (Map from the app, or atticus map --all)"
|
|
4240
|
+
});
|
|
4241
|
+
function uiCopilotRunRoutes(r, ctx, deps = {}) {
|
|
4242
|
+
const start = deps.start ?? defaultStarter(ctx.root);
|
|
4243
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
4244
|
+
const flushMs = deps.flushMs ?? 100;
|
|
4245
|
+
let run4;
|
|
4246
|
+
let allowed;
|
|
4247
|
+
let seq = 0;
|
|
4248
|
+
let starting = false;
|
|
4249
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4250
|
+
let timer;
|
|
4251
|
+
const flush = () => {
|
|
4252
|
+
timer = void 0;
|
|
4253
|
+
const r0 = run4;
|
|
4254
|
+
if (!r0)
|
|
4255
|
+
return;
|
|
4256
|
+
for (const n of pending.values())
|
|
4257
|
+
ctx.events.publish({ type: "copilot-run.lines", runId: r0.view.runId, ...n });
|
|
4258
|
+
pending.clear();
|
|
3107
4259
|
};
|
|
3108
|
-
|
|
3109
|
-
const
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
4260
|
+
const queue = (n) => {
|
|
4261
|
+
const p = pending.get(n.tab);
|
|
4262
|
+
if (p)
|
|
4263
|
+
p.lines.push(...n.lines);
|
|
4264
|
+
else
|
|
4265
|
+
pending.set(n.tab, { ...n, lines: [...n.lines] });
|
|
4266
|
+
timer ??= setTimeout(flush, flushMs);
|
|
4267
|
+
};
|
|
4268
|
+
const state = () => {
|
|
4269
|
+
if (run4)
|
|
4270
|
+
ctx.events.publish({ type: "copilot-run.state", run: run4.summary() });
|
|
4271
|
+
};
|
|
4272
|
+
return r.add("GET", "/api/copilot-runs/current", async ({ query }) => {
|
|
4273
|
+
if (!allowed || Date.now() - allowed.at > 1e4)
|
|
4274
|
+
allowed = { at: Date.now(), v: await allowedHere(ctx.root) };
|
|
4275
|
+
const a = allowed.v;
|
|
4276
|
+
const tail = Math.min(Math.max(Number(query.get("tail") ?? TAIL) || TAIL, 0), TAB_LINES);
|
|
4277
|
+
const view3 = run4 ? {
|
|
4278
|
+
...run4.summary(),
|
|
4279
|
+
tabs: run4.view.tabs.map((t) => {
|
|
4280
|
+
const keep = tail ? t.lines.slice(-tail) : t.lines;
|
|
4281
|
+
return { ...t, from: t.from + t.lines.length - keep.length, lines: [...keep] };
|
|
4282
|
+
})
|
|
4283
|
+
} : null;
|
|
3113
4284
|
return {
|
|
3114
4285
|
status: 200,
|
|
3115
4286
|
body: {
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
assistants: ASSISTANTS.map((id) => ({
|
|
3121
|
-
id,
|
|
3122
|
-
name: ASSISTANT_FORMATS[id].name,
|
|
3123
|
-
allowed: rt.assistants.allowed.includes(id),
|
|
3124
|
-
default: rt.assistants.default === id
|
|
3125
|
-
})),
|
|
3126
|
-
providers: [
|
|
3127
|
-
...Object.entries(PROVIDER_SETTINGS).map(([type, settings]) => ({
|
|
3128
|
-
type,
|
|
3129
|
-
settings: [...settings]
|
|
3130
|
-
})),
|
|
3131
|
-
// The org pack's model-provider plugins, with what each takes (ADR 0121 item 4).
|
|
3132
|
-
...op.modelProviders.map(({ manifest: m }) => ({
|
|
3133
|
-
type: m.id,
|
|
3134
|
-
settings: [
|
|
3135
|
-
...COMMON_SETTINGS,
|
|
3136
|
-
...m.auth === "none" ? [] : ["keyEnv"],
|
|
3137
|
-
...m.settings
|
|
3138
|
-
],
|
|
3139
|
-
plugin: true,
|
|
3140
|
-
pinned: !!m.sha256
|
|
3141
|
-
}))
|
|
3142
|
-
],
|
|
3143
|
-
model: model ? { type: model.type, settings: model.settings } : null,
|
|
3144
|
-
// Whether the key's variable is set here, by name: its value is never read out.
|
|
3145
|
-
keySet: keyEnv ? !!env[keyEnv]?.trim() || !!keys().get(keyEnv) : false,
|
|
3146
|
-
models: rt.models,
|
|
3147
|
-
budgets: rt.budgets,
|
|
3148
|
-
path: rel()
|
|
4287
|
+
run: view3,
|
|
4288
|
+
allowed: a.allowed,
|
|
4289
|
+
...a.allowed ? {} : { why: NOT_ALLOWED },
|
|
4290
|
+
...a.model ? { model: a.model } : {}
|
|
3149
4291
|
}
|
|
3150
4292
|
};
|
|
3151
|
-
}).add("
|
|
3152
|
-
const b = body ?? {};
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
}
|
|
3173
|
-
raw.adapters = adapters;
|
|
4293
|
+
}).add("POST", "/api/copilot-runs", async ({ body }) => {
|
|
4294
|
+
const b = api_exports.StartCopilotRunRequest.safeParse(body ?? {});
|
|
4295
|
+
if (!b.success)
|
|
4296
|
+
throw badRequest("Some fields aren't right.", {
|
|
4297
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
4298
|
+
});
|
|
4299
|
+
if (b.data.kind === "repos" && !b.data.repos?.length)
|
|
4300
|
+
throw badRequest("Name the repos to read (repos), or pick stale, gaps or all.");
|
|
4301
|
+
if (run4?.view.state === "running")
|
|
4302
|
+
throw conflict(`A Copilot run is under way (${run4.view.runId}, started ${run4.view.startedAt}): stop it, or wait for it to finish.`);
|
|
4303
|
+
if (starting)
|
|
4304
|
+
throw conflict("A Copilot run is starting: wait for it, then stop it if need be.");
|
|
4305
|
+
const busy = ctx.jobs.runningOf("step");
|
|
4306
|
+
if (busy?.state === "running")
|
|
4307
|
+
throw conflict(`An AI step is running on this server (${busy.id}): wait for it to finish.`);
|
|
4308
|
+
starting = true;
|
|
4309
|
+
let a;
|
|
4310
|
+
try {
|
|
4311
|
+
a = await allowedHere(ctx.root);
|
|
4312
|
+
} finally {
|
|
4313
|
+
starting = false;
|
|
3174
4314
|
}
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
4315
|
+
if (!a.allowed)
|
|
4316
|
+
throw conflict(NOT_ALLOWED);
|
|
4317
|
+
if (ctx.jobs.runningOf("step")?.state === "running")
|
|
4318
|
+
throw conflict("An AI step started on this server meanwhile: wait for it to finish.");
|
|
4319
|
+
const runId = `copilot-run-${now().getTime().toString(36)}-${randomBytes(3).toString("hex")}-${++seq}`;
|
|
4320
|
+
const me = new CopilotRun({
|
|
4321
|
+
runId,
|
|
4322
|
+
jobId: "",
|
|
4323
|
+
kind: b.data.kind,
|
|
4324
|
+
...b.data.repos ? { repos: b.data.repos } : {},
|
|
4325
|
+
...b.data.force ? { force: true } : {},
|
|
4326
|
+
...ctx.user ? { by: ctx.user } : {},
|
|
4327
|
+
...a.model ? { model: a.model } : {},
|
|
4328
|
+
startedAt: now().toISOString()
|
|
4329
|
+
});
|
|
4330
|
+
run4 = me;
|
|
4331
|
+
let lastErr;
|
|
4332
|
+
const job = ctx.jobs.start("step", async (c) => {
|
|
4333
|
+
const io = {
|
|
4334
|
+
out: (l) => queue(me.line(l)),
|
|
4335
|
+
err: (l) => {
|
|
4336
|
+
lastErr = l;
|
|
4337
|
+
queue(me.line(l));
|
|
4338
|
+
}
|
|
4339
|
+
};
|
|
4340
|
+
const onEvent = (e) => {
|
|
4341
|
+
for (const n of me.event(e))
|
|
4342
|
+
queue(n);
|
|
4343
|
+
const p = me.view.progress;
|
|
4344
|
+
c.progress(p.repoIndex, p.repos, p.session ?? p.repo ?? "Planning");
|
|
4345
|
+
state();
|
|
4346
|
+
};
|
|
4347
|
+
const signal = currentSignal();
|
|
4348
|
+
let code;
|
|
4349
|
+
try {
|
|
4350
|
+
code = await start(optionsOf(b.data), io, {
|
|
4351
|
+
...signal ? { signal } : {},
|
|
4352
|
+
onEvent
|
|
4353
|
+
});
|
|
4354
|
+
} catch (e) {
|
|
4355
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4356
|
+
queue(me.finish(signal?.aborted ? "stopped" : "failed", now().toISOString(), msg));
|
|
4357
|
+
flush();
|
|
4358
|
+
state();
|
|
4359
|
+
ctx.events.publish({ type: "file.changed", path: "facts" });
|
|
4360
|
+
return { errors: [msg] };
|
|
4361
|
+
}
|
|
4362
|
+
const stopped = signal?.aborted === true;
|
|
4363
|
+
queue(me.finish(stopped ? "stopped" : code === 0 ? "done" : "failed", now().toISOString(), stopped || code === 0 ? void 0 : lastErr ?? `atticus map --all ended with ${code}`));
|
|
4364
|
+
flush();
|
|
4365
|
+
state();
|
|
4366
|
+
ctx.events.publish({ type: "file.changed", path: "facts" });
|
|
4367
|
+
return { result: { code, runId } };
|
|
4368
|
+
});
|
|
4369
|
+
me.view.jobId = job.id;
|
|
4370
|
+
state();
|
|
4371
|
+
return { status: 202, body: structuredClone(me.view) };
|
|
4372
|
+
}).add("GET", "/api/copilot-runs/{runId}/tabs/{tab}", async ({ params, query }) => {
|
|
4373
|
+
if (!run4 || run4.view.runId !== params.runId)
|
|
4374
|
+
throw notFound(`There is no run ${params.runId}.`);
|
|
4375
|
+
const t = run4.view.tabs.find((x) => x.id === params.tab);
|
|
4376
|
+
if (!t)
|
|
4377
|
+
throw notFound(`${params.runId} has no tab ${params.tab}.`);
|
|
4378
|
+
const from = Math.max(Number(query.get("from") ?? 0) || 0, t.from);
|
|
4379
|
+
return {
|
|
4380
|
+
status: 200,
|
|
4381
|
+
body: { id: t.id, title: t.title, from, lines: t.lines.slice(from - t.from) }
|
|
4382
|
+
};
|
|
4383
|
+
}).add("POST", "/api/copilot-runs/{runId}/stop", async ({ params }) => {
|
|
4384
|
+
if (!run4 || run4.view.runId !== params.runId)
|
|
4385
|
+
throw notFound(`There is no run ${params.runId}.`);
|
|
4386
|
+
if (run4.view.state !== "running")
|
|
4387
|
+
throw conflict(`${params.runId} has already ${run4.view.state === "done" ? "finished" : run4.view.state}.`);
|
|
4388
|
+
ctx.jobs.cancel(run4.view.jobId);
|
|
4389
|
+
queue(run4.line("Stopping: the session under way is cancelled..."));
|
|
4390
|
+
return { status: 200, body: structuredClone(run4.view) };
|
|
4391
|
+
});
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4394
|
+
// packages/cli/dist/ui-api-link-match.js
|
|
4395
|
+
var keyOf2 = (from, to) => `${from}\0${to.repo ?? ""}\0${to.system ?? ""}`;
|
|
4396
|
+
function uiLinkMatchRoutes(r, ctx, deps = {}) {
|
|
4397
|
+
const running = /* @__PURE__ */ new Map();
|
|
4398
|
+
return r.add("GET", "/api/links/match", async ({ query }) => {
|
|
4399
|
+
const { from, to } = boxesOf(query);
|
|
4400
|
+
const { view: view3 } = await candidatesOf(ctx.root, from, to);
|
|
4401
|
+
const run4 = running.get(keyOf2(from, to));
|
|
4402
|
+
const job = run4 ? ctx.jobs.get(run4.jobId) : void 0;
|
|
4403
|
+
if (run4 && job?.state === "running") {
|
|
4404
|
+
const files = await readMatch(ctx.root, view3);
|
|
4405
|
+
return {
|
|
4406
|
+
status: 200,
|
|
4407
|
+
body: {
|
|
4408
|
+
...files,
|
|
4409
|
+
state: "running",
|
|
4410
|
+
jobId: run4.jobId,
|
|
4411
|
+
proposals: [],
|
|
4412
|
+
none: [],
|
|
4413
|
+
rejected: []
|
|
4414
|
+
}
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
const m = await readMatch(ctx.root, view3);
|
|
4418
|
+
if (run4?.error && m.state === "handoff") {
|
|
4419
|
+
m.state = "failed";
|
|
4420
|
+
m.error = run4.error;
|
|
4421
|
+
}
|
|
4422
|
+
return { status: 200, body: m };
|
|
4423
|
+
}).add("POST", "/api/links/match/done", async ({ body }) => {
|
|
4424
|
+
const b = api_exports.MatchLinksRequest.safeParse(body ?? {});
|
|
4425
|
+
if (!b.success)
|
|
4426
|
+
throw badRequest("Say both boxes: { from: <repo>, to: { repo } | { system } }.");
|
|
4427
|
+
const { from, to } = b.data;
|
|
4428
|
+
const { view: view3, facts } = await candidatesOf(ctx.root, from, to);
|
|
4429
|
+
const run4 = running.get(keyOf2(from, to));
|
|
4430
|
+
if (run4 && ctx.jobs.get(run4.jobId)?.state === "running")
|
|
4431
|
+
throw conflict(`A match of ${from} with ${view3.toName} is under way: wait for it.`);
|
|
4432
|
+
const m = await matchDone(ctx.root, { view: view3, facts });
|
|
4433
|
+
if (m.state === "handoff" || m.state === "idle")
|
|
4434
|
+
throw conflict(m.error ?? `No answer is written yet for ${from} and ${view3.toName}.`);
|
|
4435
|
+
if (m.decided?.batch) {
|
|
4436
|
+
ctx.events.publish({ type: "file.changed", path: `facts/${from}.corrections.jsonl` });
|
|
4437
|
+
ctx.events.publish({ type: "file.changed", path: "decisions/" });
|
|
4438
|
+
}
|
|
4439
|
+
return { status: 200, body: m };
|
|
4440
|
+
}).add("POST", "/api/links/match", async ({ body }) => {
|
|
4441
|
+
const b = api_exports.MatchLinksRequest.safeParse(body ?? {});
|
|
4442
|
+
if (!b.success)
|
|
4443
|
+
throw badRequest("Say both boxes: { from: <repo>, to: { repo } | { system } }.");
|
|
4444
|
+
const { from, to } = b.data;
|
|
4445
|
+
const { view: view3, facts } = await candidatesOf(ctx.root, from, to);
|
|
4446
|
+
if (!view3.calls.some((c) => c.status !== "paired"))
|
|
4447
|
+
throw conflict(`${from} has no loose call to match with ${view3.toName}.`);
|
|
4448
|
+
if (!view3.apis.length)
|
|
4449
|
+
throw conflict(`${view3.toName} has no API on record${view3.noApis ? `: ${view3.noApis.command}` : ""}.`);
|
|
4450
|
+
const key = keyOf2(from, to);
|
|
4451
|
+
const op = await loadOrgPack(orgPackDirOf(ctx.root));
|
|
4452
|
+
const had = running.get(key);
|
|
4453
|
+
if (had && ctx.jobs.get(had.jobId)?.state === "running")
|
|
4454
|
+
throw conflict(`A match of ${from} with ${view3.toName} is under way: wait for it.`);
|
|
4455
|
+
const busy = ctx.jobs.runningOf("step");
|
|
4456
|
+
if (busy?.state === "running")
|
|
4457
|
+
throw conflict(`An AI step is running on this server (${busy.id}): wait for it to finish.`);
|
|
4458
|
+
if (!copilotAcpAllowed(op) && !deps.runner) {
|
|
4459
|
+
const m = await runMatch(ctx.root, { view: view3, quotes: quotesOf(facts), mode: "hand-off" });
|
|
4460
|
+
return { status: 202, body: m };
|
|
4461
|
+
}
|
|
4462
|
+
let done;
|
|
4463
|
+
const job = ctx.jobs.start("step", async (c) => {
|
|
4464
|
+
c.progress(0, 1, `Matching ${from} with ${view3.toName}`);
|
|
4465
|
+
const signal = currentSignal();
|
|
4466
|
+
try {
|
|
4467
|
+
done = await runMatch(ctx.root, {
|
|
4468
|
+
view: view3,
|
|
4469
|
+
quotes: quotesOf(facts),
|
|
4470
|
+
facts,
|
|
4471
|
+
mode: "copilot-acp",
|
|
4472
|
+
...deps.runner ? { runner: deps.runner } : {},
|
|
4473
|
+
...signal ? { signal } : {},
|
|
4474
|
+
onProgress: (line) => c.progress(0, 1, line.slice(0, 200))
|
|
4475
|
+
});
|
|
4476
|
+
} catch (e) {
|
|
4477
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4478
|
+
const me2 = running.get(key);
|
|
4479
|
+
if (me2)
|
|
4480
|
+
me2.error = msg;
|
|
4481
|
+
return { errors: [msg] };
|
|
4482
|
+
}
|
|
4483
|
+
const me = running.get(key);
|
|
4484
|
+
if (me && done.error)
|
|
4485
|
+
me.error = done.error;
|
|
4486
|
+
return done.state === "failed" ? { errors: [done.error ?? "The session ended without an answer."] } : { result: { state: done.state } };
|
|
4487
|
+
});
|
|
4488
|
+
running.set(key, { jobId: job.id });
|
|
4489
|
+
const files = await readMatch(ctx.root, view3);
|
|
4490
|
+
return {
|
|
4491
|
+
status: 202,
|
|
4492
|
+
body: {
|
|
4493
|
+
...files,
|
|
4494
|
+
state: "running",
|
|
4495
|
+
jobId: job.id,
|
|
4496
|
+
proposals: [],
|
|
4497
|
+
none: [],
|
|
4498
|
+
rejected: []
|
|
4499
|
+
}
|
|
4500
|
+
};
|
|
4501
|
+
});
|
|
4502
|
+
}
|
|
4503
|
+
|
|
4504
|
+
// packages/cli/dist/ui-api-decisions.js
|
|
4505
|
+
var BATCH = /^b-\d{8}-\d{6}-[0-9a-f]{6}$/;
|
|
4506
|
+
function fail(e) {
|
|
4507
|
+
if (e instanceof DecideError) {
|
|
4508
|
+
if (e.code === "not-found")
|
|
4509
|
+
throw notFound(e.message);
|
|
4510
|
+
if (e.code === "conflict")
|
|
4511
|
+
throw conflict(e.message);
|
|
4512
|
+
if (e.code === "refused")
|
|
4513
|
+
throw new HttpError(422, "Atticus can't do that", e.message);
|
|
4514
|
+
throw badRequest(e.message);
|
|
4515
|
+
}
|
|
4516
|
+
throw e;
|
|
4517
|
+
}
|
|
4518
|
+
function uiDecisionRoutes(r, ctx, deps = {}) {
|
|
4519
|
+
const now = () => deps.now?.() ?? /* @__PURE__ */ new Date();
|
|
4520
|
+
return r.add("GET", "/api/decisions", async () => ({
|
|
4521
|
+
status: 200,
|
|
4522
|
+
body: await decisionsView(ctx.root)
|
|
4523
|
+
})).add("POST", "/api/decisions/correct", async ({ body }) => {
|
|
4524
|
+
const b = api_exports.CorrectDecisionRequest.safeParse(body ?? {});
|
|
4525
|
+
if (!b.success)
|
|
4526
|
+
throw badRequest("Say the item and the right answer: { item, option } for a question, { item, api, repo } or { item, none: true } for a pairing.", {
|
|
4527
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
4528
|
+
});
|
|
4529
|
+
let out;
|
|
4530
|
+
try {
|
|
4531
|
+
out = await correctDecision(ctx.root, b.data, { by: ctx.user, now: now() });
|
|
4532
|
+
} catch (e) {
|
|
4533
|
+
fail(e);
|
|
4534
|
+
}
|
|
4535
|
+
if (out.written)
|
|
4536
|
+
for (const path of out.files)
|
|
4537
|
+
ctx.events.publish({ type: "file.changed", path });
|
|
4538
|
+
return { status: 200, body: out };
|
|
4539
|
+
}).add("GET", "/api/propose-answers", async () => {
|
|
4540
|
+
const { openDecideRun, PROPOSE_CHANGE } = await import("./chunk-EFCQSMXB.js");
|
|
4541
|
+
const run4 = await openDecideRun(ctx.root, PROPOSE_CHANGE);
|
|
4542
|
+
const body = run4 ? {
|
|
4543
|
+
state: "waiting",
|
|
4544
|
+
run: run4.runId,
|
|
4545
|
+
line: "/atticus-propose",
|
|
4546
|
+
prompt: `${runsRelOf(ctx.root)}/${PROPOSE_CHANGE}/${run4.runId}/prompt.md`,
|
|
4547
|
+
done: `atticus handoff done ${run4.runId}`
|
|
4548
|
+
} : { state: "none", line: "/atticus-propose" };
|
|
4549
|
+
return { status: 200, body };
|
|
4550
|
+
}).add("POST", "/api/propose-answers", async () => {
|
|
4551
|
+
const op = await loadOrgPack(orgPackDirOf(ctx.root));
|
|
4552
|
+
if (op.runtime.ai === "off")
|
|
4553
|
+
throw conflict("runtime.yaml turns AI off here (ai: off): answer the questions yourself.");
|
|
4554
|
+
const { startProposeHandoff } = await import("./chunk-ZPDDEVQX.js");
|
|
4555
|
+
const h = await startProposeHandoff(ctx.root, { now: now() });
|
|
4556
|
+
const body = {
|
|
4557
|
+
state: "waiting",
|
|
4558
|
+
run: h.runId,
|
|
4559
|
+
line: h.line,
|
|
4560
|
+
prompt: h.promptRel,
|
|
4561
|
+
done: h.done
|
|
4562
|
+
};
|
|
4563
|
+
return { status: 201, body };
|
|
4564
|
+
}).add("POST", "/api/decisions/batches/{batchId}/undo", async ({ params }) => {
|
|
4565
|
+
const id = params.batchId ?? "";
|
|
4566
|
+
if (!BATCH.test(id))
|
|
4567
|
+
throw notFound(`There is no batch ${JSON.stringify(id)}.`);
|
|
4568
|
+
let out;
|
|
4569
|
+
try {
|
|
4570
|
+
out = await undoBatch(ctx.root, id, { by: ctx.user, now: now() });
|
|
4571
|
+
} catch (e) {
|
|
4572
|
+
fail(e);
|
|
4573
|
+
}
|
|
4574
|
+
for (const path of out.files)
|
|
4575
|
+
ctx.events.publish({ type: "file.changed", path });
|
|
4576
|
+
return { status: 200, body: out };
|
|
4577
|
+
});
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
// packages/cli/dist/ui-api-sketch.js
|
|
4581
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
4582
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
4583
|
+
import { join as join8 } from "node:path";
|
|
4584
|
+
var CHANGE = /^[a-z0-9][a-z0-9._-]{0,199}$/;
|
|
4585
|
+
var drawnOf = (deltas) => drawnStrokes(deltas);
|
|
4586
|
+
function uiSketchRoutes(r, ctx) {
|
|
4587
|
+
const root = ctx.root;
|
|
4588
|
+
const changeOf = (v) => {
|
|
4589
|
+
if (!CHANGE.test(v))
|
|
4590
|
+
throw badRequest(`${v} isn't a change id.`);
|
|
4591
|
+
return v;
|
|
4592
|
+
};
|
|
4593
|
+
const commitOf = (change) => `git add ${changesRelOf(root)}/${change}
|
|
4594
|
+
git commit -m "${safeMessage(`${change}: drawn on the canvas`)}"`;
|
|
4595
|
+
return r.add("GET", "/api/changes/{changeId}/sketch", async ({ params }) => {
|
|
4596
|
+
const change = changeOf(params.changeId);
|
|
4597
|
+
const m = await changeMapOf(root, change);
|
|
4598
|
+
if (!m)
|
|
4599
|
+
throw notFound(`There is no change ${change}.`);
|
|
4600
|
+
const dir = join8(changesDirOf(root), change);
|
|
4601
|
+
const { deltas } = await readDeltas(dir);
|
|
4602
|
+
return {
|
|
4603
|
+
status: 200,
|
|
4604
|
+
body: {
|
|
4605
|
+
written: false,
|
|
4606
|
+
files: [],
|
|
4607
|
+
map: changeMapBody(change, m),
|
|
4608
|
+
drawn: drawnOf(deltas),
|
|
4609
|
+
commit: commitOf(change),
|
|
4610
|
+
fingerprint: await sketchFingerprint(dir)
|
|
4611
|
+
}
|
|
4612
|
+
};
|
|
4613
|
+
}).add("POST", "/api/changes/{changeId}/sketch", async ({ params, body }) => {
|
|
4614
|
+
const change = changeOf(params.changeId);
|
|
4615
|
+
const b = api_exports.SketchStrokeRequest.safeParse(body ?? {});
|
|
4616
|
+
if (!b.success)
|
|
4617
|
+
throw badRequest("Say the stroke: { stroke: { kind, ... }, preview? }.");
|
|
4618
|
+
const s = Stroke.safeParse(b.data.stroke);
|
|
4619
|
+
if (!s.success)
|
|
4620
|
+
throw badRequest("The stroke isn't one Atticus draws.", {
|
|
4621
|
+
errors: s.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
4622
|
+
});
|
|
4623
|
+
const c = await changeStore(root).read(change).catch(() => void 0);
|
|
4624
|
+
if (!c)
|
|
4625
|
+
throw notFound(`There is no change ${change}.`);
|
|
4626
|
+
const dir = changeFolder(root, change);
|
|
4627
|
+
const t = await targetFlowsOf(root, change);
|
|
4628
|
+
if (!t)
|
|
4629
|
+
throw notFound(`There is no change ${change}.`);
|
|
4630
|
+
const estate = await loadEstate(root, t.records);
|
|
4631
|
+
const by = ctx.user ?? "a person";
|
|
4632
|
+
return withFileLock(join8(dir, TARGET_DIR), async () => {
|
|
4633
|
+
const fingerprint = await sketchFingerprint(dir);
|
|
4634
|
+
const { deltas, problems } = await readDeltas(dir);
|
|
4635
|
+
const answer = (extra) => ({
|
|
4636
|
+
written: false,
|
|
4637
|
+
files: [],
|
|
4638
|
+
drawn: drawnOf(deltas),
|
|
4639
|
+
commit: commitOf(change),
|
|
4640
|
+
fingerprint,
|
|
4641
|
+
...extra
|
|
4642
|
+
});
|
|
4643
|
+
if (problems.length)
|
|
4644
|
+
return {
|
|
4645
|
+
status: 200,
|
|
4646
|
+
body: answer({
|
|
4647
|
+
problem: `A target flow of ${change} doesn't read, so nothing is drawn until it is fixed: ${problems[0]}`
|
|
4648
|
+
})
|
|
4649
|
+
};
|
|
4650
|
+
const strokeId = createHash3("sha256").update(fingerprint).update(JSON.stringify(s.data)).digest("hex").slice(0, 8);
|
|
4651
|
+
const res = applyStroke(deltas, s.data, estate, by, strokeId);
|
|
4652
|
+
if (res.problem)
|
|
4653
|
+
return {
|
|
4654
|
+
status: 200,
|
|
4655
|
+
body: answer({ problem: res.problem, ...res.pick ? { pick: res.pick } : {} })
|
|
4656
|
+
};
|
|
4657
|
+
const plan = await planSketchWrite(dir, res.deltas, res.changed, estate, by);
|
|
4658
|
+
const files = (await Promise.all(plan.map(async (f) => ({
|
|
4659
|
+
file: `${changesRelOf(root)}/${change}/${f.file}`,
|
|
4660
|
+
changes: changedLines(lfOnly(await readFile6(join8(dir, ...f.file.split("/")), "utf8").catch(() => "")), lfOnly(f.now ?? ""))
|
|
4661
|
+
})))).filter((f) => f.changes.length);
|
|
4662
|
+
if (b.data.preview)
|
|
4663
|
+
return { status: 200, body: answer({ files, drawn: drawnOf(res.deltas) }) };
|
|
4664
|
+
if (b.data.expect !== void 0 && b.data.expect !== fingerprint)
|
|
4665
|
+
throw conflict(`${change}'s target flows changed since the preview: look again, then draw the stroke again.`);
|
|
4666
|
+
await writeSketch(dir, plan);
|
|
4667
|
+
for (const f of files)
|
|
4668
|
+
ctx.events.publish({ type: "file.changed", path: f.file });
|
|
4669
|
+
const m = await changeMapOf(root, change);
|
|
4670
|
+
return {
|
|
4671
|
+
status: 200,
|
|
4672
|
+
body: answer({
|
|
4673
|
+
written: true,
|
|
4674
|
+
files,
|
|
4675
|
+
...m ? { map: changeMapBody(change, m) } : {},
|
|
4676
|
+
drawn: drawnOf(res.deltas),
|
|
4677
|
+
fingerprint: await sketchFingerprint(dir)
|
|
4678
|
+
})
|
|
4679
|
+
};
|
|
4680
|
+
});
|
|
4681
|
+
});
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4684
|
+
// packages/cli/dist/ui-api-owner.js
|
|
4685
|
+
var REPO = /^[A-Za-z0-9._-]{1,200}$/;
|
|
4686
|
+
var TEAM = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
4687
|
+
function uiOwnerRoutes(r, ctx) {
|
|
4688
|
+
const options = async (repo) => {
|
|
4689
|
+
if (!REPO.test(repo))
|
|
4690
|
+
throw badRequest(`${JSON.stringify(repo)} isn't a repo name.`);
|
|
4691
|
+
const op = await loadOrgPack(orgPackDirOf(ctx.root));
|
|
4692
|
+
const entry = estateRepos(op).find((x) => x.id === repo);
|
|
4693
|
+
if (!entry)
|
|
4694
|
+
throw notFound(`No repo of the manifest is ${repo}.`);
|
|
4695
|
+
const teams = (op.roles?.teams ?? []).map((t) => ({ id: t.id, name: t.name ?? t.id }));
|
|
4696
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4697
|
+
const systems = op.profiles.flatMap((p) => p.profile?.systems ?? []).filter((s) => !seen.has(s.id) && !!seen.add(s.id)).map((s) => ({ id: s.id, name: s.name || s.id })).sort((a, b) => a.id < b.id ? -1 : 1);
|
|
4698
|
+
return { entry, teams, systems };
|
|
4699
|
+
};
|
|
4700
|
+
return r.add("GET", "/api/repos/{repo}/owner", async ({ params }) => {
|
|
4701
|
+
const repo = params.repo;
|
|
4702
|
+
const o = await options(repo);
|
|
4703
|
+
const file = (await manifestFiles(ctx.root)).find((f) => f.platform === o.entry.platform);
|
|
4704
|
+
return {
|
|
4705
|
+
status: 200,
|
|
4706
|
+
body: {
|
|
4707
|
+
repo,
|
|
4708
|
+
owner: o.entry.owner,
|
|
4709
|
+
...o.entry.system ? { system: o.entry.system } : {},
|
|
4710
|
+
file: file?.rel ?? "org-pack/profiles",
|
|
4711
|
+
teams: o.teams,
|
|
4712
|
+
systems: o.systems
|
|
4713
|
+
}
|
|
4714
|
+
};
|
|
4715
|
+
}).add("PUT", "/api/repos/{repo}/owner", async ({ params, body }) => {
|
|
4716
|
+
const repo = params.repo;
|
|
4717
|
+
const b = api_exports.SetRepoOwnerRequest.safeParse(body ?? {});
|
|
4718
|
+
if (!b.success)
|
|
4719
|
+
throw badRequest("Say the owner, the system, or a team to add: { owner, system, addTeam }.", {
|
|
4720
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
4721
|
+
});
|
|
4722
|
+
const o = await options(repo);
|
|
4723
|
+
const { addTeam, preview, system } = b.data;
|
|
4724
|
+
let owner = b.data.owner;
|
|
4725
|
+
if (addTeam) {
|
|
4726
|
+
if (!TEAM.test(addTeam.id))
|
|
4727
|
+
throw badRequest(`${addTeam.id} isn't a team id: lower case, digits and dashes, e.g. team-ledger.`);
|
|
4728
|
+
if (o.teams.some((t) => t.id === addTeam.id))
|
|
4729
|
+
throw badRequest(`${addTeam.id} is a team already: pick it as the owner.`);
|
|
4730
|
+
owner = addTeam.id;
|
|
4731
|
+
} else if (owner && !o.teams.some((t) => t.id === owner) && (o.teams.length || !TEAM.test(owner)))
|
|
4732
|
+
throw badRequest(`No team in roles.yaml is ${owner}: pick one, or add it as a new team.`);
|
|
4733
|
+
if (system && !o.systems.some((s) => s.id === system))
|
|
4734
|
+
throw badRequest(`No system of the profiles is ${system}.`);
|
|
4735
|
+
const files = [];
|
|
4736
|
+
const writtenSoFar = [];
|
|
4737
|
+
try {
|
|
4738
|
+
if (addTeam) {
|
|
4739
|
+
const t = await addRolesTeam(ctx.root, addTeam, { preview: true });
|
|
4740
|
+
files.push({ file: t.rel, changes: t.changes });
|
|
4741
|
+
}
|
|
4742
|
+
if (owner) {
|
|
4743
|
+
const m = await setManifestField(ctx.root, repo, "owner", owner, { preview: true });
|
|
4744
|
+
files.push({ file: m.file.rel, changes: m.changes });
|
|
4745
|
+
}
|
|
4746
|
+
if (system) {
|
|
4747
|
+
const m = await setManifestField(ctx.root, repo, "system", system, { preview: true });
|
|
4748
|
+
const f = files.find((x) => x.file === m.file.rel);
|
|
4749
|
+
if (f)
|
|
4750
|
+
f.changes.push(...m.changes);
|
|
4751
|
+
else
|
|
4752
|
+
files.push({ file: m.file.rel, changes: m.changes });
|
|
4753
|
+
}
|
|
4754
|
+
if (!preview) {
|
|
4755
|
+
if (owner)
|
|
4756
|
+
writtenSoFar.push((await setManifestField(ctx.root, repo, "owner", owner)).file.rel);
|
|
4757
|
+
if (system)
|
|
4758
|
+
writtenSoFar.push((await setManifestField(ctx.root, repo, "system", system)).file.rel);
|
|
4759
|
+
if (addTeam)
|
|
4760
|
+
await addRolesTeam(ctx.root, addTeam);
|
|
4761
|
+
for (const f of files)
|
|
4762
|
+
ctx.events.publish({ type: "file.changed", path: f.file });
|
|
4763
|
+
}
|
|
4764
|
+
} catch (e) {
|
|
4765
|
+
if (e instanceof FileChangedError)
|
|
4766
|
+
throw conflict(writtenSoFar.length ? `${e.message} (${[...new Set(writtenSoFar)].join(", ")} was written already; do the rest again.)` : e.message);
|
|
4767
|
+
if (e instanceof ManifestEditError)
|
|
4768
|
+
throw e.code === "not-found" ? notFound(e.message) : new HttpError(422, "Atticus can't write that", e.message);
|
|
4769
|
+
throw e;
|
|
4770
|
+
}
|
|
4771
|
+
return {
|
|
4772
|
+
status: 200,
|
|
4773
|
+
body: {
|
|
4774
|
+
written: !preview,
|
|
4775
|
+
files,
|
|
4776
|
+
verify: "atticus verify --all --write",
|
|
4777
|
+
commit: `git add ${files.map((f) => f.file).join(" ")}
|
|
4778
|
+
git commit -m "${safeMessage(`${repo}: ${[owner ? `owner ${owner}` : "", system ? `system ${system}` : ""].filter(Boolean).join(", ")}`)}"`
|
|
4779
|
+
}
|
|
4780
|
+
};
|
|
4781
|
+
});
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4784
|
+
// packages/cli/dist/ui-api-declared.js
|
|
4785
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
4786
|
+
import { join as join9 } from "node:path";
|
|
4787
|
+
var REPO2 = /^[A-Za-z0-9._-]{1,200}$/;
|
|
4788
|
+
var says = (c) => {
|
|
4789
|
+
const what = `${c.target.kind} ${c.target.value} (${c.target.file}:${c.target.line})`;
|
|
4790
|
+
if (c.action === "wrong")
|
|
4791
|
+
return `${what} is wrong: ${c.reason ?? ""}`.trim();
|
|
4792
|
+
if (c.action === "link")
|
|
4793
|
+
return `${what} goes to ${[c.link?.method, c.link?.path].filter(Boolean).join(" ")} on ${c.link?.repo ?? c.link?.system}${c.ai ? ` (detected by AI, run ${c.ai.run})` : ""}`;
|
|
4794
|
+
if (c.action === "unlink")
|
|
4795
|
+
return `${what} reaches none of these`;
|
|
4796
|
+
const set = Object.entries(c.set ?? {}).map(([k, v]) => `${k} ${v}`).join(", ");
|
|
4797
|
+
return `${what}: ${[set, c.line ? `line ${c.line}` : ""].filter(Boolean).join(", ")}`;
|
|
4798
|
+
};
|
|
4799
|
+
function uiDeclaredRoutes(r, ctx, deps = {}) {
|
|
4800
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
4801
|
+
const fail2 = (e) => {
|
|
4802
|
+
if (e instanceof FileChangedError)
|
|
4803
|
+
throw conflict(e.message);
|
|
4804
|
+
if (e instanceof DeclareError) {
|
|
4805
|
+
if (e.code === "not-found")
|
|
4806
|
+
throw notFound(e.message);
|
|
4807
|
+
if (e.code === "exists")
|
|
4808
|
+
throw conflict(e.message);
|
|
4809
|
+
throw new HttpError(422, "Atticus can't write that", e.message);
|
|
4810
|
+
}
|
|
4811
|
+
throw e;
|
|
4812
|
+
};
|
|
4813
|
+
return r.add("GET", "/api/boxes/addable", async () => {
|
|
4814
|
+
const op = await loadOrgPack(orgPackDirOf(ctx.root));
|
|
4815
|
+
const picks = targetPicker(op, await readRepos(ctx.root));
|
|
4816
|
+
return {
|
|
4817
|
+
status: 200,
|
|
4818
|
+
body: {
|
|
4819
|
+
repos: picks.filter((p) => p.kind === "repo").map((p) => ({ id: p.id, name: p.name, read: p.read === true })),
|
|
4820
|
+
systems: picks.filter((p) => p.kind === "system").map((p) => ({
|
|
4821
|
+
id: p.id,
|
|
4822
|
+
name: p.name,
|
|
4823
|
+
...p.boundary ? { boundary: p.boundary } : {}
|
|
4824
|
+
})),
|
|
4825
|
+
teams: picks.filter((p) => p.kind === "team").map((p) => ({ id: p.id, name: p.name }))
|
|
4826
|
+
}
|
|
4827
|
+
};
|
|
4828
|
+
}).add("POST", "/api/boxes", async ({ body }) => {
|
|
4829
|
+
const b = api_exports.AddSystemRequest.safeParse(body ?? {});
|
|
4830
|
+
if (!b.success)
|
|
4831
|
+
throw badRequest("Say the system's id, name and whether it is a vendor or another team's.", {
|
|
4832
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
4833
|
+
});
|
|
4834
|
+
const op = await loadOrgPack(orgPackDirOf(ctx.root));
|
|
4835
|
+
if (op.profiles.some((p) => (p.profile?.systems ?? []).some((s) => s.id === b.data.id)))
|
|
4836
|
+
throw conflict(`A system of the profiles is ${b.data.id} already: it is on the picture.`);
|
|
4837
|
+
const anyRepo = estateRepos(op)[0];
|
|
4838
|
+
if (!anyRepo)
|
|
4839
|
+
throw conflict("No repo is in a manifest yet, so there is no profile to add it to.");
|
|
4840
|
+
const sys = {
|
|
4841
|
+
id: b.data.id,
|
|
4842
|
+
name: b.data.name,
|
|
4843
|
+
boundary: b.data.boundary,
|
|
4844
|
+
...b.data.owner ? { owner: b.data.owner } : {}
|
|
4845
|
+
};
|
|
4846
|
+
if (b.data.preview) {
|
|
4847
|
+
const res = await addProfileSystem(ctx.root, anyRepo.id, sys, { preview: true }).catch((e) => {
|
|
4848
|
+
throw new HttpError(422, "Atticus can't write that", e instanceof Error ? e.message : String(e));
|
|
4849
|
+
});
|
|
4850
|
+
return {
|
|
4851
|
+
status: 200,
|
|
4852
|
+
body: {
|
|
4853
|
+
written: false,
|
|
4854
|
+
files: [{ file: res.profile, changes: res.changes ?? [] }],
|
|
4855
|
+
verify: "atticus verify --all --write",
|
|
4856
|
+
commit: ""
|
|
4857
|
+
}
|
|
4858
|
+
};
|
|
4859
|
+
}
|
|
4860
|
+
try {
|
|
4861
|
+
const res = await addProfileSystem(ctx.root, anyRepo.id, sys);
|
|
4862
|
+
ctx.events.publish({ type: "file.changed", path: res.profile });
|
|
4863
|
+
return {
|
|
4864
|
+
status: 200,
|
|
4865
|
+
body: {
|
|
4866
|
+
written: true,
|
|
4867
|
+
files: [{ file: res.profile, changes: res.changes ?? [] }],
|
|
4868
|
+
verify: "atticus verify --all --write",
|
|
4869
|
+
commit: `git add ${res.profile}
|
|
4870
|
+
git commit -m "${safeMessage(`profile: ${b.data.id} added from the canvas`)}"`
|
|
4871
|
+
}
|
|
4872
|
+
};
|
|
4873
|
+
} catch (e) {
|
|
4874
|
+
if (e instanceof FileChangedError)
|
|
4875
|
+
throw conflict(e.message);
|
|
4876
|
+
throw new HttpError(422, "Atticus can't write that", e instanceof Error ? e.message : String(e));
|
|
4877
|
+
}
|
|
4878
|
+
}).add("POST", "/api/declared-links", async ({ body }) => {
|
|
4879
|
+
const b = api_exports.DeclareLinkRequest.safeParse(body ?? {});
|
|
4880
|
+
if (!b.success)
|
|
4881
|
+
throw badRequest("Say the system and the topic with its side (publishes or listens), or the route it calls.", { errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message })) });
|
|
4882
|
+
const op = await loadOrgPack(orgPackDirOf(ctx.root));
|
|
4883
|
+
const own = estateRepos(op).some((x) => x.system === b.data.system);
|
|
4884
|
+
if (own && b.data.kind === "topic")
|
|
4885
|
+
throw conflict(`${b.data.system}'s repos are in the manifest: their code says what it publishes and listens on. Read them (atticus map) rather than declaring it.`);
|
|
4886
|
+
try {
|
|
4887
|
+
const { preview, ...d } = b.data;
|
|
4888
|
+
const res = await declareLink(ctx.root, d, {
|
|
4889
|
+
by: ctx.user,
|
|
4890
|
+
at: now().toISOString(),
|
|
4891
|
+
...preview ? { preview } : {}
|
|
4892
|
+
});
|
|
4893
|
+
if (res.written)
|
|
4894
|
+
ctx.events.publish({ type: "file.changed", path: res.rel });
|
|
4895
|
+
const what = d.kind === "topic" ? `${d.side} ${d.topic}` : `calls ${d.route}`;
|
|
4896
|
+
return {
|
|
4897
|
+
status: 200,
|
|
4898
|
+
body: {
|
|
4899
|
+
written: res.written,
|
|
4900
|
+
files: [{ file: res.rel, changes: res.changes }],
|
|
4901
|
+
verify: "atticus verify --all --write",
|
|
4902
|
+
commit: `git add ${res.rel}
|
|
4903
|
+
git commit -m "${safeMessage(`profile: ${d.system} ${what}, declared on the canvas`)}"`
|
|
4904
|
+
}
|
|
4905
|
+
};
|
|
4906
|
+
} catch (e) {
|
|
4907
|
+
return fail2(e);
|
|
4908
|
+
}
|
|
4909
|
+
}).add("POST", "/api/declared-links/undo", async ({ body }) => {
|
|
4910
|
+
const b = api_exports.UndeclareLinkRequest.safeParse(body ?? {});
|
|
4911
|
+
if (!b.success)
|
|
4912
|
+
throw badRequest("Say which declaration: { kind, system, value }.");
|
|
4913
|
+
try {
|
|
4914
|
+
const res = await undeclareLink(ctx.root, b.data);
|
|
4915
|
+
ctx.events.publish({ type: "file.changed", path: res.rel });
|
|
4916
|
+
return {
|
|
4917
|
+
status: 200,
|
|
4918
|
+
body: {
|
|
4919
|
+
written: true,
|
|
4920
|
+
files: [{ file: res.rel, changes: res.changes }],
|
|
4921
|
+
verify: "atticus verify --all --write",
|
|
4922
|
+
commit: `git add ${res.rel}
|
|
4923
|
+
git commit -m "${safeMessage(`profile: ${b.data.system}: a declaration undone`)}"`
|
|
4924
|
+
}
|
|
4925
|
+
};
|
|
4926
|
+
} catch (e) {
|
|
4927
|
+
return fail2(e);
|
|
4928
|
+
}
|
|
4929
|
+
}).add("GET", "/api/boxes/corrections", async ({ query }) => {
|
|
4930
|
+
const repo = query.get("repo");
|
|
4931
|
+
const system = query.get("system");
|
|
4932
|
+
if (!repo === !system)
|
|
4933
|
+
throw badRequest("Say repo=<repo> or system=<id>.");
|
|
4934
|
+
const items = [];
|
|
4935
|
+
if (repo) {
|
|
4936
|
+
if (!REPO2.test(repo))
|
|
4937
|
+
throw badRequest(`${JSON.stringify(repo)} isn't a repo name.`);
|
|
4938
|
+
const rel = `${factsRelOf(ctx.root)}/${repo}.corrections.jsonl`;
|
|
4939
|
+
const text = await readFile7(join9(factsDirOf(ctx.root), `${repo}.corrections.jsonl`), "utf8").catch(() => "");
|
|
4940
|
+
for (const line of text.split(/\r?\n/).filter((l) => l.trim())) {
|
|
4941
|
+
const c = parseCorrections(line).corrections[0];
|
|
4942
|
+
if (!c)
|
|
4943
|
+
continue;
|
|
4944
|
+
items.push({
|
|
4945
|
+
kind: c.action,
|
|
4946
|
+
says: says(c),
|
|
4947
|
+
by: c.by,
|
|
4948
|
+
at: c.at,
|
|
4949
|
+
...c.ai ? { ai: { runtime: c.ai.runtime, run: c.ai.run } } : {},
|
|
4950
|
+
...c.batch ? { batch: c.batch } : {},
|
|
4951
|
+
file: rel,
|
|
4952
|
+
undo: { repo, line }
|
|
4953
|
+
});
|
|
4954
|
+
}
|
|
4955
|
+
} else {
|
|
4956
|
+
const d = await declaredOf(ctx.root, system);
|
|
4957
|
+
for (const x of d.list.filter((y) => y.by))
|
|
4958
|
+
items.push({
|
|
4959
|
+
kind: x.kind,
|
|
4960
|
+
says: x.kind === "topic" ? `${x.side ? `${x.side} ` : ""}${x.value}` : `calls ${x.value}`,
|
|
4961
|
+
...x.by ? { by: x.by } : {},
|
|
4962
|
+
...x.at ? { at: x.at } : {},
|
|
4963
|
+
file: d.rel,
|
|
4964
|
+
undo: { kind: x.kind, system: x.system, value: x.value }
|
|
4965
|
+
});
|
|
4966
|
+
}
|
|
4967
|
+
return { status: 200, body: { items } };
|
|
4968
|
+
});
|
|
4969
|
+
}
|
|
4970
|
+
|
|
4971
|
+
// packages/cli/dist/ui-api-history.js
|
|
4972
|
+
function uiHistoryRoutes(r, ctx) {
|
|
4973
|
+
const root = ctx.root;
|
|
4974
|
+
return r.add("GET", "/api/history", async ({ query }) => {
|
|
4975
|
+
const limitText = query.get("limit");
|
|
4976
|
+
const limit = limitText === null ? 200 : Number(limitText);
|
|
4977
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
4978
|
+
throw badRequest("limit is a whole number of 1 or more.");
|
|
4979
|
+
const opt = (k) => {
|
|
4980
|
+
const v = query.get(k)?.trim();
|
|
4981
|
+
return v ? v : void 0;
|
|
4982
|
+
};
|
|
4983
|
+
const change = opt("change");
|
|
4984
|
+
const session = opt("session");
|
|
4985
|
+
const filter = opt("q");
|
|
4986
|
+
const body = await listHistory(root, {
|
|
4987
|
+
...change ? { change } : {},
|
|
4988
|
+
...session ? { session } : {},
|
|
4989
|
+
...filter ? { filter } : {},
|
|
4990
|
+
limit
|
|
4991
|
+
});
|
|
4992
|
+
return { status: 200, body };
|
|
4993
|
+
}).add("GET", "/api/history/{historyId}", async ({ params }) => {
|
|
4994
|
+
const e = await historyEntry(root, params.historyId);
|
|
4995
|
+
if (!e)
|
|
4996
|
+
throw notFound(`There is no record ${params.historyId}.`);
|
|
4997
|
+
return { status: 200, body: e };
|
|
4998
|
+
});
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
// packages/cli/dist/ui-api-ai.js
|
|
5002
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
5003
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
5004
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
5005
|
+
import { join as join10, relative, sep } from "node:path";
|
|
5006
|
+
var unprocessable = (detail) => new HttpError(422, "The settings aren't valid", detail);
|
|
5007
|
+
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
5008
|
+
var FIELDS = ["ai", "modes", "assistants", "models", "budgets", "decide"];
|
|
5009
|
+
function uiAiRoutes(r, ctx, deps = {}) {
|
|
5010
|
+
const root = ctx.root;
|
|
5011
|
+
const env = deps.env ?? process.env;
|
|
5012
|
+
const keys = () => deps.keys ?? defaultKeyStore();
|
|
5013
|
+
const file = () => join10(orgPackDirOf(root), "runtime.yaml");
|
|
5014
|
+
const rel = () => relative(root, file()).split(sep).join("/");
|
|
5015
|
+
const build = async (op, type, settings) => {
|
|
5016
|
+
if (deps.provider)
|
|
5017
|
+
return deps.provider(type, settings);
|
|
5018
|
+
const make = await providerFactoryFor(op, type);
|
|
5019
|
+
return make?.(settings);
|
|
5020
|
+
};
|
|
5021
|
+
return r.add("GET", "/api/settings/ai", async () => {
|
|
5022
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
5023
|
+
const rt = op.runtime;
|
|
5024
|
+
const model = rt.adapters.model;
|
|
5025
|
+
const keyEnv = typeof model?.settings.keyEnv === "string" ? model.settings.keyEnv : void 0;
|
|
5026
|
+
return {
|
|
5027
|
+
status: 200,
|
|
5028
|
+
body: {
|
|
5029
|
+
// The assistant atticus probe measured here, when one was (ADR 0124 addendum A-probe).
|
|
5030
|
+
runs: whatRunsWhere(rt, { probes: (await readProbes(root)).results }),
|
|
5031
|
+
ai: rt.ai,
|
|
5032
|
+
modes: rt.modes,
|
|
5033
|
+
assistants: ASSISTANTS.map((id) => ({
|
|
5034
|
+
id,
|
|
5035
|
+
name: ASSISTANT_FORMATS[id].name,
|
|
5036
|
+
allowed: rt.assistants.allowed.includes(id),
|
|
5037
|
+
default: rt.assistants.default === id
|
|
5038
|
+
})),
|
|
5039
|
+
providers: [
|
|
5040
|
+
...Object.entries(PROVIDER_SETTINGS).map(([type, settings]) => ({
|
|
5041
|
+
type,
|
|
5042
|
+
settings: [...settings]
|
|
5043
|
+
})),
|
|
5044
|
+
// The org pack's model-provider plugins, with what each takes (ADR 0121 item 4).
|
|
5045
|
+
...op.modelProviders.map(({ manifest: m }) => ({
|
|
5046
|
+
type: m.id,
|
|
5047
|
+
settings: [
|
|
5048
|
+
...COMMON_SETTINGS,
|
|
5049
|
+
...m.auth === "none" ? [] : ["keyEnv"],
|
|
5050
|
+
...m.settings
|
|
5051
|
+
],
|
|
5052
|
+
plugin: true,
|
|
5053
|
+
pinned: !!m.sha256
|
|
5054
|
+
}))
|
|
5055
|
+
],
|
|
5056
|
+
model: model ? { type: model.type, settings: model.settings } : null,
|
|
5057
|
+
// Whether the key's variable is set here, by name: its value is never read out.
|
|
5058
|
+
keySet: keyEnv ? !!env[keyEnv]?.trim() || !!keys().get(keyEnv) : false,
|
|
5059
|
+
models: rt.models,
|
|
5060
|
+
budgets: rt.budgets,
|
|
5061
|
+
// Ninth UX walk (ADR 0181): whether the AI decides first, and the bar that pauses it.
|
|
5062
|
+
decide: decideOf(rt),
|
|
5063
|
+
schemaVersion: rt.schemaVersion,
|
|
5064
|
+
path: rel()
|
|
5065
|
+
}
|
|
5066
|
+
};
|
|
5067
|
+
}).add("PUT", "/api/settings/ai", async ({ body }) => {
|
|
5068
|
+
const b = body ?? {};
|
|
5069
|
+
const text = existsSync3(file()) ? await readFile8(file(), "utf8") : void 0;
|
|
5070
|
+
const raw = text !== void 0 ? (0, import_yaml6.parse)(text) ?? {} : structuredClone(DEFAULT_RUNTIME);
|
|
5071
|
+
for (const f of FIELDS)
|
|
5072
|
+
if (b[f] !== void 0)
|
|
5073
|
+
raw[f] = b[f];
|
|
5074
|
+
if (b.model !== void 0) {
|
|
5075
|
+
const adapters = { ...raw.adapters ?? {} };
|
|
5076
|
+
if (b.model === null)
|
|
5077
|
+
delete adapters.model;
|
|
5078
|
+
else {
|
|
5079
|
+
const m = b.model;
|
|
5080
|
+
const settings = Object.fromEntries(Object.entries(m.settings ?? {}).filter(([, v]) => v !== "" && v !== void 0));
|
|
5081
|
+
for (const [name, v] of Object.entries(settings))
|
|
5082
|
+
for (const one of Array.isArray(v) ? v : [v])
|
|
5083
|
+
if (typeof one === "string" && looksLikeKey(one))
|
|
5084
|
+
throw unprocessable(`${name} looks like a key. A key never goes in runtime.yaml: keep it in an environment variable (or the OS's credential store) and give that variable's name as keyEnv.`);
|
|
5085
|
+
if (settings.keyEnv !== void 0 && !ENV_NAME.test(String(settings.keyEnv)))
|
|
5086
|
+
throw unprocessable(`keyEnv is the name of an environment variable, such as ATTICUS_MODEL_KEY.`);
|
|
5087
|
+
adapters.model = { type: m.type, settings };
|
|
5088
|
+
}
|
|
5089
|
+
raw.adapters = adapters;
|
|
5090
|
+
}
|
|
5091
|
+
const names = (m) => {
|
|
5092
|
+
const x = m;
|
|
5093
|
+
return [...x?.allowed ?? [], x?.default].includes("copilot-acp");
|
|
5094
|
+
};
|
|
5095
|
+
const lifted = names(b.modes) && raw.schemaVersion === 2;
|
|
5096
|
+
if (lifted)
|
|
5097
|
+
raw.schemaVersion = 3;
|
|
5098
|
+
const liftedDecide = b.decide !== void 0 && raw.schemaVersion === 3;
|
|
5099
|
+
if (liftedDecide)
|
|
5100
|
+
raw.schemaVersion = 4;
|
|
5101
|
+
const checked = Runtime.safeParse(raw);
|
|
5102
|
+
if (!checked.success)
|
|
5103
|
+
throw unprocessable(checked.error.issues.map((i) => `${i.path.join(".") || "runtime.yaml"}: ${i.message}`).join("; "));
|
|
5104
|
+
const out = (() => {
|
|
5105
|
+
if (text === void 0)
|
|
5106
|
+
return (0, import_yaml6.stringify)(raw, { lineWidth: 0 });
|
|
5107
|
+
const doc = (0, import_yaml6.parseDocument)(text);
|
|
5108
|
+
for (const f of FIELDS)
|
|
5109
|
+
if (b[f] !== void 0)
|
|
5110
|
+
doc.set(f, raw[f]);
|
|
5111
|
+
if (b.model !== void 0)
|
|
5112
|
+
doc.set("adapters", raw.adapters);
|
|
5113
|
+
if (lifted || liftedDecide)
|
|
5114
|
+
doc.set("schemaVersion", raw.schemaVersion);
|
|
5115
|
+
return doc.toString({ lineWidth: 0 });
|
|
5116
|
+
})();
|
|
5117
|
+
await writeFileIfUnchanged(file(), text, inEol(out, eolOf(text ?? "")));
|
|
5118
|
+
return {
|
|
5119
|
+
status: 200,
|
|
5120
|
+
body: {
|
|
3184
5121
|
ok: true,
|
|
3185
5122
|
path: rel(),
|
|
3186
|
-
note: `Saved ${rel()}.
|
|
5123
|
+
note: `Saved ${rel()}. Propose it for review: the org pack is reviewed like code.`
|
|
3187
5124
|
}
|
|
3188
5125
|
};
|
|
3189
5126
|
}).add("POST", "/api/settings/ai/key", async ({ body }) => {
|
|
@@ -3230,7 +5167,7 @@ function uiDocumentRoutes(r, ctx) {
|
|
|
3230
5167
|
const root = ctx.root;
|
|
3231
5168
|
const changed = (path) => ctx.events.publish({ type: "file.changed", path });
|
|
3232
5169
|
return r.add("GET", "/api/knowledge/documents", async () => {
|
|
3233
|
-
const { listDocuments } = await import("./chunk-
|
|
5170
|
+
const { listDocuments } = await import("./chunk-URIKUID7.js");
|
|
3234
5171
|
return { status: 200, body: api_exports.DocumentList.parse(await listDocuments(root)) };
|
|
3235
5172
|
}).add("POST", "/api/knowledge/documents", async ({ body }) => {
|
|
3236
5173
|
const b = api_exports.AttachFileRequest.safeParse(body ?? {});
|
|
@@ -3239,7 +5176,7 @@ function uiDocumentRoutes(r, ctx) {
|
|
|
3239
5176
|
const bytes = new Uint8Array(Buffer.from(b.data.content, "base64"));
|
|
3240
5177
|
if (bytes.length > MAX_FILE)
|
|
3241
5178
|
throw new HttpError(413, "The file is too large", "Add a file of 15 MB or less.");
|
|
3242
|
-
const { addToInbox } = await import("./chunk-
|
|
5179
|
+
const { addToInbox } = await import("./chunk-ZKYRDV7A.js");
|
|
3243
5180
|
const res = await addToInbox(root, [{ name: b.data.name, bytes }]);
|
|
3244
5181
|
if (!res.added.length)
|
|
3245
5182
|
throw new HttpError(422, "Atticus can't add that", res.refused[0]?.why ?? "", {
|
|
@@ -3249,7 +5186,7 @@ function uiDocumentRoutes(r, ctx) {
|
|
|
3249
5186
|
changed(a.path);
|
|
3250
5187
|
return { status: 201, body: api_exports.AddDocumentResult.parse(res) };
|
|
3251
5188
|
}, { maxBody: Math.ceil(MAX_FILE * 4 / 3) + 64 * 1024 }).add("POST", "/api/changes/{changeId}/inputs/{name}/promote", async ({ params }) => {
|
|
3252
|
-
const { AttachError, promoteChangeInput } = await import("./chunk-
|
|
5189
|
+
const { AttachError, promoteChangeInput } = await import("./chunk-XQ3YZ4QW.js");
|
|
3253
5190
|
try {
|
|
3254
5191
|
const res = await promoteChangeInput(root, params.changeId, params.name);
|
|
3255
5192
|
changed(res.path);
|
|
@@ -3266,13 +5203,13 @@ function uiDocumentRoutes(r, ctx) {
|
|
|
3266
5203
|
function uiRepoRoutes(r, ctx) {
|
|
3267
5204
|
const root = ctx.root;
|
|
3268
5205
|
return r.add("GET", "/api/repos/branches", async () => {
|
|
3269
|
-
const { repoBranches } = await import("./chunk-
|
|
5206
|
+
const { repoBranches } = await import("./chunk-YBOVNMHC.js");
|
|
3270
5207
|
return { status: 200, body: api_exports.RepoBranchesList.parse(await repoBranches(root)) };
|
|
3271
5208
|
}).add("GET", "/api/repos/compare", async ({ query }) => {
|
|
3272
5209
|
const branch = query.get("branch")?.trim() ?? "";
|
|
3273
5210
|
if (!branch)
|
|
3274
5211
|
throw badRequest("Say which branch: ?branch=<name>.");
|
|
3275
|
-
const { compareBranch } = await import("./chunk-
|
|
5212
|
+
const { compareBranch } = await import("./chunk-YBOVNMHC.js");
|
|
3276
5213
|
const c = await compareBranch(root, branch);
|
|
3277
5214
|
if (!c)
|
|
3278
5215
|
throw notFound(`${branch} hasn't been scanned beside the baseline yet.`);
|
|
@@ -3281,7 +5218,7 @@ function uiRepoRoutes(r, ctx) {
|
|
|
3281
5218
|
const b = api_exports.EditRepoBranchesRequest.safeParse(body ?? {});
|
|
3282
5219
|
if (!b.success)
|
|
3283
5220
|
throw badRequest("Send defaultBranch and branches as branch names.");
|
|
3284
|
-
const { editRepoBranches, RepoEditError } = await import("./chunk-
|
|
5221
|
+
const { editRepoBranches, RepoEditError } = await import("./chunk-YBOVNMHC.js");
|
|
3285
5222
|
try {
|
|
3286
5223
|
const file = await editRepoBranches(root, params.repoId, b.data);
|
|
3287
5224
|
ctx.events.publish({ type: "file.changed", path: file });
|
|
@@ -3294,14 +5231,56 @@ function uiRepoRoutes(r, ctx) {
|
|
|
3294
5231
|
};
|
|
3295
5232
|
} catch (e) {
|
|
3296
5233
|
if (e instanceof RepoEditError)
|
|
3297
|
-
throw e.code === "not-found" ? notFound(e.message) : badRequest(e.message);
|
|
5234
|
+
throw e.code === "not-found" ? notFound(e.message) : e.code === "conflict" ? conflict(e.message) : badRequest(e.message);
|
|
5235
|
+
throw e;
|
|
5236
|
+
}
|
|
5237
|
+
}).add("PUT", "/api/repos/{repoId}/build", async ({ params, body }) => {
|
|
5238
|
+
const b = api_exports.EditRepoBuildRequest.safeParse(body ?? {});
|
|
5239
|
+
if (!b.success)
|
|
5240
|
+
throw badRequest('Send the build command as build ("" clears it), up to 500 characters.');
|
|
5241
|
+
const { editRepoBuild, RepoEditError } = await import("./chunk-YBOVNMHC.js");
|
|
5242
|
+
try {
|
|
5243
|
+
const r2 = await editRepoBuild(root, params.repoId, b.data);
|
|
5244
|
+
ctx.events.publish({ type: "file.changed", path: r2.file });
|
|
5245
|
+
return {
|
|
5246
|
+
status: 200,
|
|
5247
|
+
body: {
|
|
5248
|
+
file: r2.file,
|
|
5249
|
+
...r2.build ? { build: r2.build } : {},
|
|
5250
|
+
note: r2.build ? `${r2.file} changed: propose it for review. A task in ${params.repoId} is now built and tested with it before its PR is raised; run the task again.` : `${r2.file} changed: propose it for review. ${params.repoId} has no build command now, so its tasks aren't built or tested.`
|
|
5251
|
+
}
|
|
5252
|
+
};
|
|
5253
|
+
} catch (e) {
|
|
5254
|
+
if (e instanceof RepoEditError)
|
|
5255
|
+
throw e.code === "not-found" ? notFound(e.message) : e.code === "conflict" ? conflict(e.message) : badRequest(e.message);
|
|
5256
|
+
throw e;
|
|
5257
|
+
}
|
|
5258
|
+
}).add("PUT", "/api/orgpack/owner", async ({ body }) => {
|
|
5259
|
+
const b = api_exports.SetOrgPackOwnerRequest.safeParse(body ?? {});
|
|
5260
|
+
if (!b.success)
|
|
5261
|
+
throw badRequest("Send the owner as one handle: { owner }.");
|
|
5262
|
+
const { setOrgPackOwner, OwnerEditError } = await import("./chunk-PQH56YF6.js");
|
|
5263
|
+
try {
|
|
5264
|
+
const r2 = await setOrgPackOwner(root, b.data.owner, b.data.was);
|
|
5265
|
+
ctx.events.publish({ type: "file.changed", path: r2.file });
|
|
5266
|
+
return {
|
|
5267
|
+
status: 200,
|
|
5268
|
+
body: {
|
|
5269
|
+
file: r2.file,
|
|
5270
|
+
owner: r2.owner,
|
|
5271
|
+
note: `${r2.owner} now looks after the org pack. ${r2.file} changed: propose it for review; its owner approves changes to the org pack.`
|
|
5272
|
+
}
|
|
5273
|
+
};
|
|
5274
|
+
} catch (e) {
|
|
5275
|
+
if (e instanceof OwnerEditError)
|
|
5276
|
+
throw e.code === "not-found" ? notFound(e.message) : e.code === "conflict" ? conflict(e.message) : badRequest(e.message);
|
|
3298
5277
|
throw e;
|
|
3299
5278
|
}
|
|
3300
5279
|
}).add("PUT", "/api/repos/{repoId}/description", async ({ params, body }) => {
|
|
3301
5280
|
const b = api_exports.EditRepoDescriptionRequest.safeParse(body ?? {});
|
|
3302
5281
|
if (!b.success)
|
|
3303
5282
|
throw badRequest("Send what the repo is as text, or accept: true to confirm the draft, or reject: true to drop it.");
|
|
3304
|
-
const { editRepoDescription, RepoEditError } = await import("./chunk-
|
|
5283
|
+
const { editRepoDescription, RepoEditError } = await import("./chunk-YBOVNMHC.js");
|
|
3305
5284
|
try {
|
|
3306
5285
|
const r2 = await editRepoDescription(root, params.repoId, b.data);
|
|
3307
5286
|
ctx.events.publish({ type: "file.changed", path: r2.file });
|
|
@@ -3315,7 +5294,7 @@ function uiRepoRoutes(r, ctx) {
|
|
|
3315
5294
|
};
|
|
3316
5295
|
} catch (e) {
|
|
3317
5296
|
if (e instanceof RepoEditError)
|
|
3318
|
-
throw e.code === "not-found" ? notFound(e.message) : badRequest(e.message);
|
|
5297
|
+
throw e.code === "not-found" ? notFound(e.message) : e.code === "conflict" ? conflict(e.message) : badRequest(e.message);
|
|
3319
5298
|
throw e;
|
|
3320
5299
|
}
|
|
3321
5300
|
});
|
|
@@ -3324,11 +5303,11 @@ function uiRepoRoutes(r, ctx) {
|
|
|
3324
5303
|
// packages/cli/dist/ui-api-cockpit.js
|
|
3325
5304
|
function uiCockpitRoutes(r, ctx) {
|
|
3326
5305
|
return r.add("GET", "/api/changes/{changeId}/cockpit", async ({ params }) => {
|
|
3327
|
-
const { cockpit } = await import("./chunk-
|
|
5306
|
+
const { cockpit } = await import("./chunk-6EXDI5GI.js");
|
|
3328
5307
|
try {
|
|
3329
5308
|
return {
|
|
3330
5309
|
status: 200,
|
|
3331
|
-
body: api_exports.ChangeCockpit.parse(await cockpit(ctx.root, params.changeId))
|
|
5310
|
+
body: api_exports.ChangeCockpit.parse(await cockpit(ctx.root, params.changeId, { user: ctx.user }))
|
|
3332
5311
|
};
|
|
3333
5312
|
} catch (e) {
|
|
3334
5313
|
if (e instanceof Error && e.name === "ChangeFileError")
|
|
@@ -3336,17 +5315,17 @@ function uiCockpitRoutes(r, ctx) {
|
|
|
3336
5315
|
throw e;
|
|
3337
5316
|
}
|
|
3338
5317
|
}).add("GET", "/api/past-changes", async ({ query }) => {
|
|
3339
|
-
const { changeAboutText, similarChanges } = await import("./chunk-
|
|
5318
|
+
const { changeAboutText, similarChanges } = await import("./chunk-BC6KKZ67.js");
|
|
3340
5319
|
const change = query.get("change");
|
|
3341
|
-
const
|
|
3342
|
-
if (change && !
|
|
5320
|
+
const text = change ? await changeAboutText(ctx.root, change) : query.get("text")?.trim();
|
|
5321
|
+
if (change && !text)
|
|
3343
5322
|
throw notFound(`There is no change ${change}.`);
|
|
3344
|
-
if (!
|
|
5323
|
+
if (!text)
|
|
3345
5324
|
throw badRequest("Say what the new change is about: ?text=<its title or brief>, or ?change=<a change's id>.");
|
|
3346
5325
|
const limit = Number(query.get("limit") ?? 5);
|
|
3347
5326
|
return {
|
|
3348
5327
|
status: 200,
|
|
3349
|
-
body: api_exports.SimilarChanges.parse(await similarChanges(ctx.root,
|
|
5328
|
+
body: api_exports.SimilarChanges.parse(await similarChanges(ctx.root, text.slice(0, 2e4), {
|
|
3350
5329
|
limit: Number.isInteger(limit) && limit > 0 && limit <= 20 ? limit : 5
|
|
3351
5330
|
}))
|
|
3352
5331
|
};
|
|
@@ -3354,11 +5333,11 @@ function uiCockpitRoutes(r, ctx) {
|
|
|
3354
5333
|
const b = api_exports.AttachPastChangeRequest.safeParse(body ?? {});
|
|
3355
5334
|
if (!b.success)
|
|
3356
5335
|
throw badRequest("Say which archived change: { like: <its folder or id> }.");
|
|
3357
|
-
const { attachPastChange, findPastChange } = await import("./chunk-
|
|
5336
|
+
const { attachPastChange, findPastChange } = await import("./chunk-BC6KKZ67.js");
|
|
3358
5337
|
const folder = await findPastChange(ctx.root, b.data.like);
|
|
3359
5338
|
if (!folder)
|
|
3360
5339
|
throw notFound(`There is no archived change ${b.data.like}.`);
|
|
3361
|
-
const { AttachError } = await import("./chunk-
|
|
5340
|
+
const { AttachError } = await import("./chunk-XQ3YZ4QW.js");
|
|
3362
5341
|
try {
|
|
3363
5342
|
const name = await attachPastChange(ctx.root, params.changeId, folder, ctx.user);
|
|
3364
5343
|
ctx.events.publish({ type: "file.changed", path: `inputs/${name}` });
|
|
@@ -3374,396 +5353,521 @@ function uiCockpitRoutes(r, ctx) {
|
|
|
3374
5353
|
});
|
|
3375
5354
|
}
|
|
3376
5355
|
|
|
3377
|
-
// packages/cli/dist/ui-api-
|
|
3378
|
-
var
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
const m = /^gitdir:\s*(.+)$/m.exec(await text(git) ?? "");
|
|
3394
|
-
if (!m)
|
|
3395
|
-
return void 0;
|
|
3396
|
-
git = resolve2(dir, m[1].trim());
|
|
3397
|
-
}
|
|
3398
|
-
const common = (await text(join9(git, "commondir")))?.trim();
|
|
3399
|
-
const commonDir = common ? resolve2(git, common) : git;
|
|
3400
|
-
const head = (await text(join9(git, "HEAD")) ?? "").trim();
|
|
3401
|
-
if (FULL_SHA.test(head))
|
|
3402
|
-
return head;
|
|
3403
|
-
const ref = /^ref:\s*(refs\/[A-Za-z0-9._/-]+)$/.exec(head)?.[1];
|
|
3404
|
-
if (!ref || ref.split("/").includes(".."))
|
|
3405
|
-
return void 0;
|
|
3406
|
-
for (const d of [git, commonDir]) {
|
|
3407
|
-
const loose = (await text(join9(d, ...ref.split("/"))) ?? "").trim();
|
|
3408
|
-
if (FULL_SHA.test(loose))
|
|
3409
|
-
return loose;
|
|
3410
|
-
}
|
|
3411
|
-
for (const line of (await text(join9(commonDir, "packed-refs")) ?? "").split(/\r?\n/)) {
|
|
3412
|
-
const [sha2, name] = line.split(" ");
|
|
3413
|
-
if (name === ref && sha2 && FULL_SHA.test(sha2))
|
|
3414
|
-
return sha2;
|
|
3415
|
-
}
|
|
3416
|
-
return void 0;
|
|
5356
|
+
// packages/cli/dist/ui-api-audit.js
|
|
5357
|
+
var REPO3 = /^[a-z0-9][a-z0-9._-]{0,99}$/i;
|
|
5358
|
+
var repoOf = (v) => {
|
|
5359
|
+
if (!REPO3.test(v))
|
|
5360
|
+
throw badRequest(`${JSON.stringify(v)} isn't a repo name.`);
|
|
5361
|
+
return v;
|
|
5362
|
+
};
|
|
5363
|
+
async function itemsWithLines(root, repo, items) {
|
|
5364
|
+
const text = await auditLines(root, repo, items);
|
|
5365
|
+
return items.map((i) => ({
|
|
5366
|
+
...i,
|
|
5367
|
+
lines: i.cites.map((c) => {
|
|
5368
|
+
const t = text.get(c);
|
|
5369
|
+
return { cite: c, ...t ? { text: t.length > 300 ? `${t.slice(0, 297)}...` : t } : {} };
|
|
5370
|
+
})
|
|
5371
|
+
}));
|
|
3417
5372
|
}
|
|
3418
|
-
|
|
3419
|
-
const
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
const
|
|
3424
|
-
return {
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
5373
|
+
var ofDraw = (audit, drawn) => {
|
|
5374
|
+
const byId = new Map(audit.items.map((i) => [i.id, i]));
|
|
5375
|
+
return drawn.map((d) => byId.get(d.id) ?? d);
|
|
5376
|
+
};
|
|
5377
|
+
function uiAuditRoutes(r, ctx, deps = {}) {
|
|
5378
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
5379
|
+
return r.add("GET", "/api/facts/{repo}/audit", async ({ params, query }) => {
|
|
5380
|
+
const repo = repoOf(params.repo);
|
|
5381
|
+
const seed = query.get("seed") || void 0;
|
|
5382
|
+
const sizeText = query.get("size");
|
|
5383
|
+
const size = sizeText ? Number(sizeText) : void 0;
|
|
5384
|
+
if (size !== void 0 && (!Number.isInteger(size) || size < 1 || size > 100))
|
|
5385
|
+
throw badRequest("size is a whole number from 1 to 100.");
|
|
5386
|
+
const p = await prepareAudit(ctx.root, repo, {
|
|
5387
|
+
...seed ? { seed } : {},
|
|
5388
|
+
...size ? { size } : {},
|
|
5389
|
+
now
|
|
5390
|
+
});
|
|
5391
|
+
const command = (s, n) => `atticus verify audit ${repo} --seed ${s} --size ${n}`;
|
|
5392
|
+
if ("refused" in p) {
|
|
5393
|
+
const body2 = {
|
|
5394
|
+
repo,
|
|
5395
|
+
file: `${factsRelOf(ctx.root)}/${repo}.audit.yaml`,
|
|
5396
|
+
seed: seed ?? "",
|
|
5397
|
+
size: size ?? 10,
|
|
5398
|
+
items: [],
|
|
5399
|
+
refused: p.refused,
|
|
5400
|
+
...p.unverified ? { unverified: p.unverified } : {},
|
|
5401
|
+
command: `atticus verify audit ${repo}`
|
|
5402
|
+
};
|
|
5403
|
+
return { status: 200, body: body2 };
|
|
5404
|
+
}
|
|
5405
|
+
const precision = await precisionOf(ctx.root, repo, p.audit, p.facts);
|
|
5406
|
+
const commit = [...new Set(p.facts.map((f) => f.commit))].sort()[0];
|
|
5407
|
+
const body = {
|
|
5408
|
+
repo,
|
|
5409
|
+
file: p.fileRel,
|
|
5410
|
+
seed: p.seed,
|
|
5411
|
+
size: p.size,
|
|
5412
|
+
defaultSeed: defaultAuditSeed(repo, commit),
|
|
5413
|
+
items: await itemsWithLines(ctx.root, repo, ofDraw(p.audit, p.drawn)),
|
|
5414
|
+
precision: { facts: precision.facts, links: precision.links },
|
|
5415
|
+
command: command(p.seed, p.size)
|
|
5416
|
+
};
|
|
5417
|
+
return { status: 200, body };
|
|
5418
|
+
}).add("POST", "/api/facts/{repo}/audit", async ({ params, body }) => {
|
|
5419
|
+
const repo = repoOf(params.repo);
|
|
5420
|
+
const b = api_exports.AuditAnswerRequest.safeParse(body ?? {});
|
|
5421
|
+
if (!b.success)
|
|
5422
|
+
throw badRequest("Say the draw and the answers: { seed, size, answers: [{ id, answer, note }] }.", {
|
|
5423
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
5424
|
+
});
|
|
5425
|
+
const p = await prepareAudit(ctx.root, repo, {
|
|
5426
|
+
seed: b.data.seed,
|
|
5427
|
+
size: b.data.size,
|
|
5428
|
+
now
|
|
5429
|
+
});
|
|
5430
|
+
if ("refused" in p) {
|
|
5431
|
+
if (p.refused === "no-facts")
|
|
5432
|
+
throw notFound(p.lines.join(" "));
|
|
5433
|
+
throw new HttpError(422, "Atticus can't audit this repo yet", p.lines.join(" "));
|
|
5434
|
+
}
|
|
5435
|
+
const inDraw = new Set(p.drawn.map((d) => d.id));
|
|
5436
|
+
const outside = b.data.answers.filter((a) => !inDraw.has(a.id)).map((a) => a.id);
|
|
5437
|
+
if (outside.length)
|
|
5438
|
+
throw new HttpError(422, "Not in this sample", `Not in this sample: ${outside.join(", ")}. Answer the items the draw lists.`);
|
|
5439
|
+
const answers = new Map(b.data.answers.map((a) => [
|
|
5440
|
+
a.id,
|
|
5441
|
+
{ answer: a.answer, ...a.note ? { note: a.note } : {} }
|
|
5442
|
+
]));
|
|
5443
|
+
answerAudit(p.audit, answers, ctx.user, p.now);
|
|
5444
|
+
await writeAudit(ctx.root, repo, p.audit);
|
|
5445
|
+
ctx.events.publish({ type: "file.changed", path: p.fileRel });
|
|
5446
|
+
const precision = await precisionOf(ctx.root, repo, p.audit, p.facts);
|
|
5447
|
+
const result = {
|
|
5448
|
+
file: p.fileRel,
|
|
5449
|
+
answered: p.audit.items.filter((i) => i.answer).length,
|
|
5450
|
+
open: p.audit.items.filter((i) => !i.answer).length,
|
|
5451
|
+
items: await itemsWithLines(ctx.root, repo, ofDraw(p.audit, p.drawn)),
|
|
5452
|
+
precision: { facts: precision.facts, links: precision.links },
|
|
5453
|
+
commit: `git add ${p.fileRel}
|
|
5454
|
+
git commit -m "${repo}: a person's sample audit answers"`
|
|
5455
|
+
};
|
|
5456
|
+
return { status: 200, body: result };
|
|
5457
|
+
});
|
|
3437
5458
|
}
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
const
|
|
3463
|
-
const
|
|
3464
|
-
const
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
const named = /* @__PURE__ */ new Set();
|
|
3475
|
-
for (const f of files) {
|
|
3476
|
-
const of = factsFileOf(f);
|
|
3477
|
-
if (of && (of.facts || f.endsWith(".verify.yaml")))
|
|
3478
|
-
named.add(of.repo);
|
|
5459
|
+
|
|
5460
|
+
// packages/cli/dist/ui-api-outside.js
|
|
5461
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
5462
|
+
import { join as join11 } from "node:path";
|
|
5463
|
+
var SPECS = /* @__PURE__ */ new Set(["openapi", "swagger"]);
|
|
5464
|
+
var cmp = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
5465
|
+
var OUTSIDE_ADD_SPEC = (id) => [
|
|
5466
|
+
`atticus intake add <the ${id} spec file>`,
|
|
5467
|
+
"atticus intake list",
|
|
5468
|
+
"atticus triage"
|
|
5469
|
+
];
|
|
5470
|
+
async function outsideView(root) {
|
|
5471
|
+
const quiet = { out: () => void 0, err: () => void 0 };
|
|
5472
|
+
const { op, records, inputs } = await intakeContext(root, quiet);
|
|
5473
|
+
const systems = outsideProfileSystems(op);
|
|
5474
|
+
const stamp2 = await stampOf(root);
|
|
5475
|
+
const estate = await estateFacts(root, op, "");
|
|
5476
|
+
const facts = /* @__PURE__ */ new Map();
|
|
5477
|
+
for (const r of estate.repos)
|
|
5478
|
+
for (const f of r.facts)
|
|
5479
|
+
facts.set(`${r.repo}\0${f.id}`, f);
|
|
5480
|
+
const joined = await estateJoinOnFile(root, op, estate, stamp2);
|
|
5481
|
+
const repos = joinReposOf(op, estate.repos, componentRenames(estate.repos, op));
|
|
5482
|
+
const { maps: sharedMaps } = await readSharedMaps(root);
|
|
5483
|
+
const sharedJoin = sharedMaps.length ? joinSharedMaps({ outside: joined.outside ?? [], repos, maps: sharedMaps, systems }) : { matches: [], gaps: [], asks: [] };
|
|
5484
|
+
const specs = /* @__PURE__ */ new Map();
|
|
5485
|
+
for (const d of inputs.documents) {
|
|
5486
|
+
if (!d.id || d.supersededBy || !SPECS.has(d.classification))
|
|
5487
|
+
continue;
|
|
5488
|
+
const system = systems.find((s) => s.id === d.systems[0]);
|
|
5489
|
+
if (!system || specs.has(system.id))
|
|
5490
|
+
continue;
|
|
5491
|
+
const body = await readFile9(join11(inputsDirOf(root), ...d.path.split("/").slice(1)), "utf8");
|
|
5492
|
+
const spec = readVendorSpec(d.path, body, system.id);
|
|
5493
|
+
if (spec)
|
|
5494
|
+
specs.set(system.id, spec);
|
|
3479
5495
|
}
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
5496
|
+
const vendors = systems.flatMap((s) => {
|
|
5497
|
+
const spec = specs.get(s.id);
|
|
5498
|
+
if (!spec)
|
|
5499
|
+
return [];
|
|
5500
|
+
return [
|
|
5501
|
+
{
|
|
5502
|
+
system: s.id,
|
|
5503
|
+
boundary: "internal",
|
|
5504
|
+
names: [s.name, ...s.aliases],
|
|
5505
|
+
hosts: [.../* @__PURE__ */ new Set([...spec.hosts, ...hostAliases(s.aliases)])],
|
|
5506
|
+
operations: spec.operations.map((o) => ({
|
|
5507
|
+
id: o.id,
|
|
5508
|
+
method: o.method,
|
|
5509
|
+
path: o.path,
|
|
5510
|
+
fullPath: o.fullPath,
|
|
5511
|
+
cite: o.cite
|
|
5512
|
+
}))
|
|
5513
|
+
}
|
|
5514
|
+
];
|
|
5515
|
+
});
|
|
5516
|
+
const matched = vendors.length ? joinVendorOperations({
|
|
5517
|
+
repos,
|
|
5518
|
+
vendors,
|
|
5519
|
+
targets: estate.targets,
|
|
5520
|
+
...joinConventions(op)
|
|
5521
|
+
}) : { matches: [], gaps: [], asks: [] };
|
|
5522
|
+
const contracts = await contractCheckOf(root).catch(() => void 0);
|
|
5523
|
+
const catalog = records.length ? records : (await readCatalog(join11(catalogDirOf(root), "generated"), { cacheRoot: root })).records;
|
|
5524
|
+
const ifaces = catalog.filter((r) => r.kind === "Interface");
|
|
5525
|
+
const componentsOf = (id) => /* @__PURE__ */ new Set([
|
|
5526
|
+
id,
|
|
5527
|
+
...catalog.filter((r) => r.kind === "Component" && r.system === id).map((r) => r.id)
|
|
5528
|
+
]);
|
|
5529
|
+
const open = await listQuestions(root, { state: "open" }).then((l) => l.questions, () => []);
|
|
5530
|
+
const callOf = (e) => {
|
|
5531
|
+
const f = facts.get(`${e.repo}\0${e.fact}`);
|
|
5532
|
+
const x = f?.kind === "call" ? f : void 0;
|
|
5533
|
+
return {
|
|
5534
|
+
repo: e.repo,
|
|
5535
|
+
...e.component ? { component: e.component } : {},
|
|
5536
|
+
fact: e.fact,
|
|
5537
|
+
at: e.at,
|
|
5538
|
+
...x?.method ? { method: x.method.toUpperCase() } : {},
|
|
5539
|
+
...x?.path ? { path: x.path } : {}
|
|
5540
|
+
};
|
|
3507
5541
|
};
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
const
|
|
3524
|
-
if (
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
if (commit)
|
|
3529
|
-
facts.commit = commit;
|
|
3530
|
-
const gaps = new Map(frameworkGaps(parsed.facts).map((g) => [g.id, g.question]));
|
|
3531
|
-
const built = frameworkFacts(parsed.facts).map((f) => ({
|
|
3532
|
-
name: f.name,
|
|
3533
|
-
...f.version ? { version: f.version } : {},
|
|
3534
|
-
...f.component ? { component: f.component } : {},
|
|
3535
|
-
cite: `${f.cite.file}:${f.cite.line}`,
|
|
3536
|
-
...gaps.has(f.id) ? { gap: gaps.get(f.id) } : {}
|
|
3537
|
-
}));
|
|
3538
|
-
if (built.length)
|
|
3539
|
-
frameworks = built;
|
|
5542
|
+
const contractsOf = (id) => {
|
|
5543
|
+
const findings = contracts?.result.findings.filter((f) => f.system === id) ?? [];
|
|
5544
|
+
const fresh = contracts?.newBySystem.get(id);
|
|
5545
|
+
if (!findings.length && !fresh)
|
|
5546
|
+
return {};
|
|
5547
|
+
return {
|
|
5548
|
+
contracts: { findings, ...fresh ? { new: fresh.map((f) => f.id) } : {} }
|
|
5549
|
+
};
|
|
5550
|
+
};
|
|
5551
|
+
const out = systems.map((s) => {
|
|
5552
|
+
const names = new Set([s.id, s.name, ...s.aliases].map(factIdPart));
|
|
5553
|
+
const comps = componentsOf(s.id);
|
|
5554
|
+
const callers = /* @__PURE__ */ new Map();
|
|
5555
|
+
const add = (c, seen) => {
|
|
5556
|
+
const k = `${c.repo}|${c.at}`;
|
|
5557
|
+
const had = callers.get(k);
|
|
5558
|
+
if (had) {
|
|
5559
|
+
if (!had.seen.includes(seen))
|
|
5560
|
+
had.seen = [...had.seen, seen].sort();
|
|
5561
|
+
Object.assign(had, { ...c, ...had, seen: had.seen });
|
|
3540
5562
|
} else
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
const
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
5563
|
+
callers.set(k, { ...c, seen: [seen] });
|
|
5564
|
+
};
|
|
5565
|
+
for (const o of joined.outside ?? [])
|
|
5566
|
+
if (names.has(factIdPart(o.target)))
|
|
5567
|
+
add(callOf(o.call), "join");
|
|
5568
|
+
for (const r of ifaces) {
|
|
5569
|
+
if (!comps.has(r.to))
|
|
5570
|
+
continue;
|
|
5571
|
+
for (const e of r.evidence)
|
|
5572
|
+
if (e.side !== "callee" && e.source !== "config" && e.repo && e.path && e.line !== void 0)
|
|
5573
|
+
add({
|
|
5574
|
+
repo: e.repo,
|
|
5575
|
+
component: r.from,
|
|
5576
|
+
at: `${e.path}:${e.line}`,
|
|
5577
|
+
...r.method ? { method: r.method } : {},
|
|
5578
|
+
path: r.normalizedPath,
|
|
5579
|
+
interface: r.id
|
|
5580
|
+
}, "interface");
|
|
3559
5581
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
5582
|
+
for (const g of [...matched.gaps, ...matched.asks].filter((x) => x.vendor === s.id))
|
|
5583
|
+
add(callOf(g.call), "spec");
|
|
5584
|
+
for (const m of matched.matches.filter((x) => x.vendor === s.id)) {
|
|
5585
|
+
add({ ...callOf(m.call), operation: m.operation }, "spec");
|
|
5586
|
+
if (m.direct || !m.route)
|
|
5587
|
+
continue;
|
|
5588
|
+
const route = m.route;
|
|
5589
|
+
for (const j of joined.joins)
|
|
5590
|
+
if (j.entry.repo === route.repo && j.entry.fact === route.fact)
|
|
5591
|
+
add({
|
|
5592
|
+
...callOf(j.call),
|
|
5593
|
+
operation: m.operation,
|
|
5594
|
+
through: { repo: route.repo, at: route.at }
|
|
5595
|
+
}, "spec");
|
|
3567
5596
|
}
|
|
3568
|
-
const
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
5597
|
+
for (const m of sharedJoin.matches.filter((x) => x.system === s.id))
|
|
5598
|
+
add({
|
|
5599
|
+
...callOf(m.call),
|
|
5600
|
+
entry: { team: m.team, repo: m.entry.repo, fact: m.entry.fact, at: m.entry.at }
|
|
5601
|
+
}, "shared");
|
|
5602
|
+
for (const g of sharedJoin.gaps.filter((x) => x.system === s.id))
|
|
5603
|
+
add(callOf(g.call), "shared");
|
|
5604
|
+
const list = [...callers.values()].sort((a, b) => cmp(a.repo, b.repo) || cmp(a.at, b.at));
|
|
5605
|
+
const spec = specs.get(s.id);
|
|
5606
|
+
const map = sharedMaps.find((m) => m.system === s.id);
|
|
5607
|
+
const lines = new Set(list.map((c) => `${c.repo}|${c.at}`));
|
|
5608
|
+
const named = [s.id, s.name, ...s.aliases].map((n) => n.toLowerCase());
|
|
5609
|
+
const questions = open.filter((q) => named.some((n) => q.question.toLowerCase().includes(n)) || q.options.some((o) => named.some((n) => o.label.toLowerCase().includes(n))) || q.evidence.some((e) => lines.has(`${q.repo}|${e}`))).map((q) => ({ id: q.id, repo: q.repo, question: q.question })).sort((a, b) => cmp(a.id, b.id));
|
|
5610
|
+
return {
|
|
5611
|
+
id: s.id,
|
|
5612
|
+
name: s.name,
|
|
5613
|
+
boundary: s.boundary,
|
|
5614
|
+
...s.owner ? { owner: s.owner } : {},
|
|
5615
|
+
aliases: [...s.aliases],
|
|
5616
|
+
callers: list,
|
|
5617
|
+
...spec ? {
|
|
5618
|
+
spec: {
|
|
5619
|
+
path: spec.path,
|
|
5620
|
+
...spec.title ? { title: spec.title } : {},
|
|
5621
|
+
...spec.version ? { version: spec.version } : {},
|
|
5622
|
+
operations: spec.operations.map((o) => ({
|
|
5623
|
+
id: o.id,
|
|
5624
|
+
method: o.method,
|
|
5625
|
+
path: o.fullPath,
|
|
5626
|
+
cite: o.cite,
|
|
5627
|
+
calls: list.filter((c) => c.operation === o.id).length
|
|
5628
|
+
}))
|
|
5629
|
+
}
|
|
5630
|
+
} : {},
|
|
5631
|
+
unmatched: matched.gaps.filter((g) => g.vendor === s.id).map((g) => ({ ...callOf(g.call), seen: ["spec"], message: g.message })),
|
|
5632
|
+
ambiguous: matched.asks.filter((a) => a.vendor === s.id).map((a) => ({ ...callOf(a.call), seen: ["spec"], candidates: a.candidates })),
|
|
5633
|
+
questions,
|
|
5634
|
+
...spec ? {} : { addSpec: OUTSIDE_ADD_SPEC(s.id) },
|
|
5635
|
+
...contractsOf(s.id),
|
|
5636
|
+
...map ? {
|
|
5637
|
+
shared: {
|
|
5638
|
+
team: map.team,
|
|
5639
|
+
name: map.name,
|
|
5640
|
+
commit: map.commit,
|
|
5641
|
+
importedAt: map.file.importedAt,
|
|
5642
|
+
entries: map.repos.flatMap((r) => r.facts.flatMap((f) => f.kind === "entry" ? [
|
|
5643
|
+
{
|
|
5644
|
+
repo: r.repo,
|
|
5645
|
+
component: factIdPart(f.component ?? r.component ?? r.repo),
|
|
5646
|
+
fact: f.id,
|
|
5647
|
+
...f.method ? { method: f.method.toUpperCase() } : {},
|
|
5648
|
+
path: f.path,
|
|
5649
|
+
at: `${f.cite.file}:${f.cite.line}`,
|
|
5650
|
+
...f.commit ? { commit: f.commit } : {},
|
|
5651
|
+
calls: list.filter((c) => c.entry?.repo === r.repo && c.entry.fact === f.id).length
|
|
5652
|
+
}
|
|
5653
|
+
] : [])).sort((a, b) => cmp(a.repo, b.repo) || cmp(a.at, b.at)),
|
|
5654
|
+
unmatched: sharedJoin.gaps.filter((g) => g.system === s.id).map((g) => ({ ...callOf(g.call), seen: ["shared"], message: g.message }))
|
|
5655
|
+
}
|
|
5656
|
+
} : {}
|
|
3574
5657
|
};
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
const base = {
|
|
3578
|
-
repo,
|
|
3579
|
-
facts,
|
|
3580
|
-
verify: { path: rel(reportPath), report },
|
|
3581
|
-
checkout,
|
|
3582
|
-
sameCommit: sameCommit(report?.commit ?? facts.commit, head),
|
|
3583
|
-
catalog: { from, fromFacts: c.facts, fromReaders: c.readers }
|
|
3584
|
-
};
|
|
3585
|
-
const { state, next } = nextOf(repo, base);
|
|
3586
|
-
repos.push({
|
|
3587
|
-
...base,
|
|
3588
|
-
state,
|
|
3589
|
-
...next ? { next } : {},
|
|
3590
|
-
...frameworks ? { frameworks } : {}
|
|
3591
|
-
});
|
|
3592
|
-
}
|
|
3593
|
-
return { factsDir: factsRel, repos, problems };
|
|
5658
|
+
});
|
|
5659
|
+
return { systems: out, notes: estate.notes };
|
|
3594
5660
|
}
|
|
3595
|
-
function
|
|
3596
|
-
return r.add("GET", "/api/
|
|
5661
|
+
function uiOutsideRoutes(r, ctx) {
|
|
5662
|
+
return r.add("GET", "/api/outside", async () => ({
|
|
5663
|
+
status: 200,
|
|
5664
|
+
body: await outsideView(ctx.root)
|
|
5665
|
+
}));
|
|
3597
5666
|
}
|
|
3598
5667
|
|
|
3599
|
-
// packages/cli/dist/ui-api-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
var fixed = new Set(FACT_FIXED_FIELDS);
|
|
3604
|
-
function factLabel(f) {
|
|
3605
|
-
switch (f.kind) {
|
|
3606
|
-
case "entry":
|
|
3607
|
-
return `${f.method ? `${f.method} ` : ""}${f.path}`;
|
|
3608
|
-
case "call":
|
|
3609
|
-
return `${f.method ? `${f.method} ` : ""}${f.path}${f.to ? ` \u2192 ${f.to}` : ""}`;
|
|
3610
|
-
case "store":
|
|
3611
|
-
return `${f.name} (${f.op})`;
|
|
3612
|
-
case "field":
|
|
3613
|
-
return `${f.name}${f.type ? `: ${f.type}` : ""}`;
|
|
3614
|
-
case "deploy":
|
|
3615
|
-
return `${f.platform}${f.resourceName ? ` ${f.resourceName}` : ""}`;
|
|
3616
|
-
case "config":
|
|
3617
|
-
return `${f.key}${f.value !== void 0 ? ` = ${f.value}` : ""}`;
|
|
3618
|
-
// ADR 0124 addendum A-framework: what the repo or the module is built on.
|
|
3619
|
-
case "framework":
|
|
3620
|
-
return `${f.name}${f.version ? ` ${f.version}` : ""}`;
|
|
3621
|
-
case "flow":
|
|
3622
|
-
return f.name;
|
|
3623
|
-
}
|
|
5668
|
+
// packages/cli/dist/ui-api-contracts.js
|
|
5669
|
+
async function contractsView(root, check = contractCheckOf) {
|
|
5670
|
+
const { result, notes } = await check(root);
|
|
5671
|
+
return { links: result.links, findings: result.findings, counts: result.counts, notes };
|
|
3624
5672
|
}
|
|
3625
|
-
function
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
5673
|
+
function uiContractsRoutes(r, ctx, o = {}) {
|
|
5674
|
+
return r.add("GET", "/api/contracts", async () => ({
|
|
5675
|
+
status: 200,
|
|
5676
|
+
body: await contractsView(ctx.root, o.check)
|
|
5677
|
+
}));
|
|
5678
|
+
}
|
|
5679
|
+
|
|
5680
|
+
// packages/cli/dist/ui-api-stages.js
|
|
5681
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
5682
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
5683
|
+
import { readFile as readFile11, stat as stat6 } from "node:fs/promises";
|
|
5684
|
+
import { join as join13 } from "node:path";
|
|
5685
|
+
|
|
5686
|
+
// packages/cli/dist/ui-api-drafts.js
|
|
5687
|
+
import { readdir as readdir4, readFile as readFile10, stat as stat5 } from "node:fs/promises";
|
|
5688
|
+
import { join as join12 } from "node:path";
|
|
5689
|
+
var ID3 = /^[a-z0-9][a-z0-9._-]{0,99}$/;
|
|
5690
|
+
var CORRECTION_ID = /^c-[0-9a-f]{10}$/;
|
|
5691
|
+
var exists2 = (p) => stat5(p).then(() => true, () => false);
|
|
5692
|
+
var PAGE_STAGE = {
|
|
5693
|
+
feature: "feature",
|
|
5694
|
+
architecture: "architecture",
|
|
5695
|
+
stories: "stories",
|
|
5696
|
+
tasks: "stories"
|
|
5697
|
+
};
|
|
5698
|
+
var ON_PAGE = {
|
|
5699
|
+
feature: (f) => f === "feature.md" || f === "proposal.md",
|
|
5700
|
+
architecture: (f) => /^target-flows\/[^/]+\.ya?ml$/.test(f) || f === "design.md" || f === "data.md",
|
|
5701
|
+
stories: (f) => f === "stories.md" || /^stories\/[^/]+\.md$/.test(f) || /^specs\/.+\/spec\.md$/.test(f),
|
|
5702
|
+
tasks: (f) => f === "tasks.md"
|
|
5703
|
+
};
|
|
5704
|
+
var pageOfFile = (f) => Object.keys(ON_PAGE).find((p) => ON_PAGE[p](f));
|
|
5705
|
+
async function walk(dir, sub = "") {
|
|
5706
|
+
const out = [];
|
|
5707
|
+
for (const e of await readdir4(join12(dir, sub), { withFileTypes: true }).catch(() => [])) {
|
|
5708
|
+
if (e.name.startsWith("."))
|
|
5709
|
+
continue;
|
|
5710
|
+
const rel = sub ? `${sub}/${e.name}` : e.name;
|
|
5711
|
+
if (e.isDirectory()) {
|
|
5712
|
+
if (["stage", "review", "tasks", "inputs"].includes(rel))
|
|
5713
|
+
continue;
|
|
5714
|
+
out.push(...await walk(dir, rel));
|
|
5715
|
+
} else
|
|
5716
|
+
out.push(rel);
|
|
5717
|
+
}
|
|
3631
5718
|
return out;
|
|
3632
5719
|
}
|
|
3633
|
-
var
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
5720
|
+
var slashOf = (stage, change) => `/atticus-${stage === "small" ? "small" : stage} ${change}`;
|
|
5721
|
+
var nextOf = (stage, change) => ({
|
|
5722
|
+
stage,
|
|
5723
|
+
command: rerunOf(stage, change),
|
|
5724
|
+
slash: slashOf(stage, change)
|
|
3638
5725
|
});
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
const
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
facts: p.facts,
|
|
3649
|
-
bad: p.bad.map((b) => ({
|
|
3650
|
-
file,
|
|
3651
|
-
message: `line ${b.line} isn't a fact: ${b.issues.map((i) => i.message).join("; ")}`
|
|
3652
|
-
}))
|
|
3653
|
-
};
|
|
5726
|
+
var view = (c) => ({ ...c });
|
|
5727
|
+
async function writtenByStage(dir, change) {
|
|
5728
|
+
const out = [];
|
|
5729
|
+
for (const s of STAGES) {
|
|
5730
|
+
const l = await readStageLoop(dir, change, s).catch(() => void 0);
|
|
5731
|
+
if (l?.present && l.loop.written)
|
|
5732
|
+
out.push({ stage: s, hashes: l.loop.written });
|
|
5733
|
+
}
|
|
5734
|
+
return out;
|
|
3654
5735
|
}
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
const
|
|
3658
|
-
const
|
|
3659
|
-
const problems = [
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
}))
|
|
3665
|
-
];
|
|
3666
|
-
const facts = f.facts.map((x) => {
|
|
3667
|
-
const corr = correctionFor(x, c.corrections);
|
|
3668
|
-
return {
|
|
3669
|
-
id: x.id,
|
|
3670
|
-
kind: x.kind,
|
|
3671
|
-
label: factLabel(x),
|
|
3672
|
-
claim: claimOf(x),
|
|
3673
|
-
cite: spot(x.cite),
|
|
3674
|
-
...x.kind === "flow" ? {
|
|
3675
|
-
steps: [
|
|
3676
|
-
...x.steps.map((s) => ({ ...spot(s.cite), what: s.what })),
|
|
3677
|
-
...x.errors.map((e) => ({ ...spot(e.cite), what: `${e.when} \u2192 ${e.then}` }))
|
|
3678
|
-
]
|
|
3679
|
-
} : {},
|
|
3680
|
-
verified: x.verified,
|
|
3681
|
-
by: x.by,
|
|
3682
|
-
...corr ? { correction: corr } : {}
|
|
3683
|
-
};
|
|
5736
|
+
var provenanceView = (p) => ({ ...p });
|
|
5737
|
+
async function stageDraftsView(root, change, page) {
|
|
5738
|
+
const dir = changeFolder(root, change);
|
|
5739
|
+
const rel = `${changesRelOf(root)}/${change}`;
|
|
5740
|
+
const problems = [];
|
|
5741
|
+
const candidates = (await walk(dir)).filter(ON_PAGE[page]).sort();
|
|
5742
|
+
const prov = await draftProvenance(dir, change, candidates, await writtenByStage(dir, change)).catch((e) => {
|
|
5743
|
+
problems.push({ file: `${rel}/stage/drafted.yaml`, message: String(e.message) });
|
|
5744
|
+
return {};
|
|
3684
5745
|
});
|
|
3685
|
-
const
|
|
3686
|
-
|
|
5746
|
+
const files = [];
|
|
5747
|
+
for (const f of candidates) {
|
|
5748
|
+
const p = prov[f];
|
|
5749
|
+
if (!p)
|
|
5750
|
+
continue;
|
|
5751
|
+
const text = await readFile10(join12(dir, ...f.split("/")), "utf8").catch(() => "") ?? "";
|
|
5752
|
+
files.push({
|
|
5753
|
+
file: f,
|
|
5754
|
+
path: `${rel}/${f}`,
|
|
5755
|
+
stage: p.by === "ai" ? p.stage : PAGE_STAGE[page],
|
|
5756
|
+
drafted: provenanceView(p),
|
|
5757
|
+
items: draftItems(f, text)
|
|
5758
|
+
});
|
|
5759
|
+
}
|
|
5760
|
+
let corrections = [];
|
|
5761
|
+
try {
|
|
5762
|
+
corrections = await readCorrections(dir, change);
|
|
5763
|
+
} catch (e) {
|
|
5764
|
+
problems.push({ file: `${rel}/${CORRECTIONS_FILE}`, message: String(e.message) });
|
|
5765
|
+
}
|
|
5766
|
+
const mine = corrections.filter((c) => ON_PAGE[page](c.file)).sort((a, b) => a.at < b.at ? -1 : a.at > b.at ? 1 : 0);
|
|
5767
|
+
const waiting = mine.find((c) => c.state === "waiting");
|
|
3687
5768
|
return {
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
corrections: { file: cFile, count: c.corrections.length, stale: [...new Set(stale)].sort() },
|
|
3694
|
-
questions,
|
|
3695
|
-
verify: `atticus verify ${repo}`,
|
|
5769
|
+
change,
|
|
5770
|
+
page,
|
|
5771
|
+
files,
|
|
5772
|
+
corrections: mine.map(view),
|
|
5773
|
+
next: nextOf(waiting?.stage ?? PAGE_STAGE[page], change),
|
|
3696
5774
|
problems
|
|
3697
5775
|
};
|
|
3698
5776
|
}
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
5777
|
+
function pageOf(v) {
|
|
5778
|
+
const r = api_exports.DraftPage.safeParse(v);
|
|
5779
|
+
if (!r.success)
|
|
5780
|
+
throw badRequest(`${JSON.stringify(v)} isn't a page of drafts: feature, architecture, stories or tasks.`);
|
|
5781
|
+
return r.data;
|
|
5782
|
+
}
|
|
5783
|
+
var refusal = (e) => {
|
|
5784
|
+
if (e instanceof PlannerError && e.code === "refused")
|
|
5785
|
+
throw conflict(e.message);
|
|
5786
|
+
if (e instanceof PlannerError)
|
|
5787
|
+
throw new HttpError(422, "Atticus refused", e.message);
|
|
5788
|
+
throw e;
|
|
3703
5789
|
};
|
|
3704
|
-
function
|
|
5790
|
+
function uiDraftRoutes(r, ctx, deps = {}) {
|
|
3705
5791
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
5792
|
+
const changeOf = async (v) => {
|
|
5793
|
+
if (!ID3.test(v))
|
|
5794
|
+
throw badRequest(`${JSON.stringify(v)} isn't a change id.`);
|
|
5795
|
+
if (!await exists2(join12(changeFolder(ctx.root, v), "change.yaml")))
|
|
5796
|
+
throw notFound(`There is no change ${v}.`);
|
|
5797
|
+
return v;
|
|
5798
|
+
};
|
|
5799
|
+
const list = async (change) => ({
|
|
5800
|
+
change,
|
|
5801
|
+
corrections: (await readCorrections(changeFolder(ctx.root, change), change).catch(refusal)).map(view)
|
|
5802
|
+
});
|
|
5803
|
+
return r.add("GET", "/api/changes/{changeId}/drafts/{page}", async ({ params }) => {
|
|
5804
|
+
const page = pageOf(params.page);
|
|
5805
|
+
const change = await changeOf(params.changeId);
|
|
5806
|
+
return { status: 200, body: await stageDraftsView(ctx.root, change, page) };
|
|
5807
|
+
}).add("GET", "/api/changes/{changeId}/corrections", async ({ params }) => {
|
|
5808
|
+
const change = await changeOf(params.changeId);
|
|
5809
|
+
return { status: 200, body: await list(change) };
|
|
5810
|
+
}).add("POST", "/api/changes/{changeId}/corrections", async ({ params, body }) => {
|
|
5811
|
+
const change = await changeOf(params.changeId);
|
|
5812
|
+
const b = api_exports.AddCorrectionRequest.safeParse(body ?? {});
|
|
3712
5813
|
if (!b.success)
|
|
3713
|
-
throw badRequest("Say which
|
|
5814
|
+
throw badRequest("Say which draft (file, and item) and write the correction: { file, item, text }.", {
|
|
3714
5815
|
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
3715
5816
|
});
|
|
3716
|
-
const
|
|
3717
|
-
if (!
|
|
3718
|
-
throw notFound(
|
|
3719
|
-
const
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
const
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
5817
|
+
const page = pageOfFile(b.data.file);
|
|
5818
|
+
if (!page)
|
|
5819
|
+
throw notFound(`${b.data.file} isn't a stage draft of ${change}.`);
|
|
5820
|
+
const v = await stageDraftsView(ctx.root, change, page);
|
|
5821
|
+
const f = v.files.find((x) => x.file === b.data.file);
|
|
5822
|
+
if (!f)
|
|
5823
|
+
throw notFound(`${b.data.file} isn't a stage draft of ${change}.`);
|
|
5824
|
+
const item = b.data.item ? f.items.find((i) => i.id === b.data.item) : void 0;
|
|
5825
|
+
if (b.data.item && !item)
|
|
5826
|
+
throw notFound(`${b.data.file} has no item ${b.data.item} now: reload the page, then correct it again.`);
|
|
5827
|
+
const stage = f.stage;
|
|
5828
|
+
const dir = changeFolder(ctx.root, change);
|
|
5829
|
+
const { present } = await readStageLoop(dir, change, stage);
|
|
5830
|
+
if (!present)
|
|
5831
|
+
throw conflict(`The ${stage} stage of ${change} hasn't run here yet, so nothing would read a correction: run ${rerunOf(stage, change)} first.`);
|
|
5832
|
+
const c = await addCorrection(dir, change, {
|
|
5833
|
+
stage,
|
|
5834
|
+
file: f.file,
|
|
5835
|
+
...item ? { item: item.id } : {},
|
|
5836
|
+
label: item?.label ?? f.file,
|
|
5837
|
+
text: b.data.text,
|
|
5838
|
+
similar: b.data.similar,
|
|
5839
|
+
...b.data.like ? { like: b.data.like } : item?.like?.[0] ? { like: item.like[0] } : {},
|
|
3738
5840
|
by: ctx.user,
|
|
3739
5841
|
at: now().toISOString()
|
|
3740
|
-
});
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
if (c.data.action === "edit") {
|
|
3744
|
-
const fixedFact = correctedFact(fact, c.data);
|
|
3745
|
-
if (typeof fixedFact === "string")
|
|
3746
|
-
throw new HttpError(422, "That isn't a fact", `The corrected ${fact.kind} isn't one: ${fixedFact}.`);
|
|
3747
|
-
}
|
|
3748
|
-
const rel = `${factsRelOf(ctx.root)}/${repo}.corrections.jsonl`;
|
|
3749
|
-
await mkdir(factsDirOf(ctx.root), { recursive: true });
|
|
3750
|
-
await appendFile(join10(factsDirOf(ctx.root), `${repo}.corrections.jsonl`), `${correctionLine(c.data)}
|
|
3751
|
-
`, "utf8");
|
|
3752
|
-
ctx.events.publish({ type: "file.changed", path: rel });
|
|
5842
|
+
}).catch(refusal);
|
|
5843
|
+
const written = `${changesRelOf(ctx.root)}/${change}/${CORRECTIONS_FILE}`;
|
|
5844
|
+
ctx.events.publish({ type: "file.changed", path: written });
|
|
3753
5845
|
const result = {
|
|
3754
|
-
correction: c
|
|
3755
|
-
|
|
3756
|
-
commit: `git add ${
|
|
3757
|
-
|
|
5846
|
+
correction: view(c),
|
|
5847
|
+
files: [written],
|
|
5848
|
+
commit: `git add ${written}
|
|
5849
|
+
git commit -m "${change}: a correction of ${f.file}"`,
|
|
5850
|
+
next: nextOf(stage, change)
|
|
3758
5851
|
};
|
|
3759
5852
|
return { status: 200, body: result };
|
|
5853
|
+
}).add("POST", "/api/changes/{changeId}/corrections/{correctionId}/remove", async ({ params }) => {
|
|
5854
|
+
const change = await changeOf(params.changeId);
|
|
5855
|
+
const id = params.correctionId;
|
|
5856
|
+
if (!CORRECTION_ID.test(id))
|
|
5857
|
+
throw badRequest(`${JSON.stringify(id)} isn't a correction id.`);
|
|
5858
|
+
const gone = await removeCorrection(changeFolder(ctx.root, change), change, id).catch(refusal);
|
|
5859
|
+
if (!gone)
|
|
5860
|
+
throw notFound(`Change ${change} has no correction ${id}.`);
|
|
5861
|
+
ctx.events.publish({
|
|
5862
|
+
type: "file.changed",
|
|
5863
|
+
path: `${changesRelOf(ctx.root)}/${change}/${CORRECTIONS_FILE}`
|
|
5864
|
+
});
|
|
5865
|
+
return { status: 200, body: await list(change) };
|
|
3760
5866
|
});
|
|
3761
5867
|
}
|
|
3762
5868
|
|
|
3763
5869
|
// packages/cli/dist/ui-api-stages.js
|
|
3764
|
-
|
|
3765
|
-
import { join as join11 } from "node:path";
|
|
3766
|
-
var ID2 = /^[a-z0-9][a-z0-9._-]{0,99}$/;
|
|
5870
|
+
var ID4 = /^[a-z0-9][a-z0-9._-]{0,99}$/;
|
|
3767
5871
|
function findingOptions(f) {
|
|
3768
5872
|
const open = f.state === "open";
|
|
3769
5873
|
const issue = f.kind === "issue";
|
|
@@ -3775,8 +5879,8 @@ function findingOptions(f) {
|
|
|
3775
5879
|
...open && f.question ? { ask: { text: f.question.text, owner: f.question.owner } } : {}
|
|
3776
5880
|
};
|
|
3777
5881
|
}
|
|
3778
|
-
var
|
|
3779
|
-
var
|
|
5882
|
+
var exists3 = (p) => stat6(p).then(() => true, () => false);
|
|
5883
|
+
var slashOf2 = (stage, change) => `/atticus-${stage} ${change}`;
|
|
3780
5884
|
async function gateOf(root, change, stage) {
|
|
3781
5885
|
const problems = [];
|
|
3782
5886
|
let gate;
|
|
@@ -3802,7 +5906,7 @@ async function latest(root, change, gate) {
|
|
|
3802
5906
|
const r = await readRound(root, change, gate, round);
|
|
3803
5907
|
return { round, findings: r.findings, bad: r.bad };
|
|
3804
5908
|
}
|
|
3805
|
-
var
|
|
5909
|
+
var view2 = (c) => ({
|
|
3806
5910
|
question: c.question,
|
|
3807
5911
|
text: c.text,
|
|
3808
5912
|
by: c.by,
|
|
@@ -3826,7 +5930,7 @@ async function stageReviewView(root, change, stage) {
|
|
|
3826
5930
|
finding: s.finding,
|
|
3827
5931
|
file: fileRel,
|
|
3828
5932
|
options: findingOptions(s.finding),
|
|
3829
|
-
comments: comments.comments.filter((c) => c.about === fileRel).map(
|
|
5933
|
+
comments: comments.comments.filter((c) => c.about === fileRel).map(view2),
|
|
3830
5934
|
...p ? {
|
|
3831
5935
|
proposal: {
|
|
3832
5936
|
claim: p.claim,
|
|
@@ -3859,10 +5963,37 @@ async function stageReviewView(root, change, stage) {
|
|
|
3859
5963
|
},
|
|
3860
5964
|
findings,
|
|
3861
5965
|
questions,
|
|
3862
|
-
next: { command: rerunOf(stage, change), slash:
|
|
5966
|
+
next: { command: rerunOf(stage, change), slash: slashOf2(stage, change) },
|
|
3863
5967
|
problems
|
|
3864
5968
|
};
|
|
3865
5969
|
}
|
|
5970
|
+
var PLACED_HEAD = "# The feature placed against the estate (ADR 0087).";
|
|
5971
|
+
var EDITED = /^# The feature placed against the estate \(ADR 0087\)\. Edited by (.+) on (\S+); /;
|
|
5972
|
+
var sha3 = (s) => createHash4("sha256").update(s).digest("hex");
|
|
5973
|
+
function draftedOf(text) {
|
|
5974
|
+
const head = text.split("\n", 1)[0] ?? "";
|
|
5975
|
+
if (head.startsWith(PLACED_HEAD) && / Drafted by /.test(head))
|
|
5976
|
+
return { by: "ai" };
|
|
5977
|
+
const m = EDITED.exec(head);
|
|
5978
|
+
return m ? { by: "person", who: m[1], at: m[2] } : { by: "person" };
|
|
5979
|
+
}
|
|
5980
|
+
async function placementView(root, change) {
|
|
5981
|
+
const rel = `${changesRelOf(root)}/${change}`;
|
|
5982
|
+
const dir = changeFolder(root, change);
|
|
5983
|
+
const read = (f) => readFile11(join13(dir, f), "utf8").catch(() => void 0);
|
|
5984
|
+
const source = await read(FEATURE_FILE);
|
|
5985
|
+
const content = await read("feature.md") ?? "";
|
|
5986
|
+
return {
|
|
5987
|
+
change,
|
|
5988
|
+
present: source !== void 0,
|
|
5989
|
+
file: `${rel}/feature.md`,
|
|
5990
|
+
content,
|
|
5991
|
+
source: `${rel}/${FEATURE_FILE}`,
|
|
5992
|
+
sourceContent: source ?? "",
|
|
5993
|
+
sha256: sha3(source ?? ""),
|
|
5994
|
+
...source !== void 0 ? { drafted: draftedOf(source) } : {}
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
3866
5997
|
function tabOf(v) {
|
|
3867
5998
|
const r = api_exports.StageTab.safeParse(v);
|
|
3868
5999
|
if (!r.success)
|
|
@@ -3872,13 +6003,57 @@ function tabOf(v) {
|
|
|
3872
6003
|
function uiStageRoutes(r, ctx, deps = {}) {
|
|
3873
6004
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
3874
6005
|
const changeOf = async (v) => {
|
|
3875
|
-
if (!
|
|
6006
|
+
if (!ID4.test(v))
|
|
3876
6007
|
throw badRequest(`${JSON.stringify(v)} isn't a change id.`);
|
|
3877
|
-
if (!await
|
|
6008
|
+
if (!await exists3(join13(changeFolder(ctx.root, v), "change.yaml")))
|
|
3878
6009
|
throw notFound(`There is no change ${v}.`);
|
|
3879
6010
|
return v;
|
|
3880
6011
|
};
|
|
3881
|
-
return r.add("GET", "/api/changes/{changeId}/
|
|
6012
|
+
return uiDraftRoutes(r, ctx, deps).add("GET", "/api/changes/{changeId}/placement", async ({ params }) => {
|
|
6013
|
+
const change = await changeOf(params.changeId);
|
|
6014
|
+
return { status: 200, body: await placementView(ctx.root, change) };
|
|
6015
|
+
}).add("PUT", "/api/changes/{changeId}/placement", async ({ params, body }) => {
|
|
6016
|
+
const change = await changeOf(params.changeId);
|
|
6017
|
+
const b = api_exports.SavePlacementRequest.safeParse(body ?? {});
|
|
6018
|
+
if (!b.success)
|
|
6019
|
+
throw badRequest("Send feature.yaml's text and the sha256 it started from.", {
|
|
6020
|
+
errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
|
|
6021
|
+
});
|
|
6022
|
+
const dir = changeFolder(ctx.root, change);
|
|
6023
|
+
const file = join13(dir, FEATURE_FILE);
|
|
6024
|
+
const was = await readFile11(file, "utf8").catch(() => void 0);
|
|
6025
|
+
if (sha3(was ?? "") !== b.data.baseSha256)
|
|
6026
|
+
throw conflict(`${FEATURE_FILE} changed since you opened it. Reload it, then edit again.`);
|
|
6027
|
+
const lines = b.data.content.replace(/\r\n/g, "\n").split("\n");
|
|
6028
|
+
const own = lines[0]?.startsWith(PLACED_HEAD) ? lines.slice(1) : lines;
|
|
6029
|
+
let parsed;
|
|
6030
|
+
try {
|
|
6031
|
+
parsed = (0, import_yaml7.parse)(own.join("\n"));
|
|
6032
|
+
} catch (e) {
|
|
6033
|
+
throw new HttpError(422, "Not a placement", `${FEATURE_FILE} doesn't read as YAML: ${String(e.message ?? e).split("\n")[0]}`);
|
|
6034
|
+
}
|
|
6035
|
+
const p = FeaturePlacement.safeParse(parsed);
|
|
6036
|
+
if (!p.success)
|
|
6037
|
+
throw new HttpError(422, "Not a placement", `${FEATURE_FILE} doesn't read as a placement: ${p.error.issues.map((i) => `${i.path.join(".") || "(top)"}: ${i.message}`).join("; ")}`);
|
|
6038
|
+
if (p.data.change !== change)
|
|
6039
|
+
throw new HttpError(422, "Not a placement", `${FEATURE_FILE} names change ${p.data.change}, not ${change}.`);
|
|
6040
|
+
const at = now().toISOString();
|
|
6041
|
+
const head = `${PLACED_HEAD} Edited by ${ctx.user} on ${at}; review it in the feature PR.`;
|
|
6042
|
+
const saved = `${head}
|
|
6043
|
+
${own.join("\n")}`;
|
|
6044
|
+
await writeFileIfUnchanged(file, was, saved);
|
|
6045
|
+
const md = renderFeature(p.data);
|
|
6046
|
+
await writeFileAtomic(join13(dir, "feature.md"), md);
|
|
6047
|
+
for (const [f, content] of [
|
|
6048
|
+
[FEATURE_FILE, saved],
|
|
6049
|
+
["feature.md", md]
|
|
6050
|
+
])
|
|
6051
|
+
await recordPersonSave(dir, change, { file: f, who: ctx.user, at, content }).catch(() => void 0);
|
|
6052
|
+
const rel = `${changesRelOf(ctx.root)}/${change}`;
|
|
6053
|
+
for (const f of [FEATURE_FILE, "feature.md"])
|
|
6054
|
+
ctx.events.publish({ type: "file.changed", path: `${rel}/${f}` });
|
|
6055
|
+
return { status: 200, body: await placementView(ctx.root, change) };
|
|
6056
|
+
}).add("GET", "/api/changes/{changeId}/stages/{stage}", async ({ params }) => {
|
|
3882
6057
|
const stage = tabOf(params.stage);
|
|
3883
6058
|
const change = await changeOf(params.changeId);
|
|
3884
6059
|
return { status: 200, body: await stageReviewView(ctx.root, change, stage) };
|
|
@@ -3926,12 +6101,13 @@ function uiStageRoutes(r, ctx, deps = {}) {
|
|
|
3926
6101
|
comment: {
|
|
3927
6102
|
question: q.id,
|
|
3928
6103
|
text: q.answer.text,
|
|
3929
|
-
by: q.answer.by,
|
|
6104
|
+
by: answerByWords(q.answer.by),
|
|
3930
6105
|
at: q.answer.at,
|
|
3931
6106
|
read: false
|
|
3932
6107
|
},
|
|
3933
6108
|
files: written.files,
|
|
3934
|
-
commit: `git add ${written.files.join(" ")}
|
|
6109
|
+
commit: `git add ${written.files.join(" ")}
|
|
6110
|
+
git commit -m "${change}: a comment on ${f.finding.id}"`,
|
|
3935
6111
|
next: v.next
|
|
3936
6112
|
};
|
|
3937
6113
|
return { status: 200, body: result };
|
|
@@ -3942,13 +6118,23 @@ function uiStageRoutes(r, ctx, deps = {}) {
|
|
|
3942
6118
|
function catalogMayHaveChanged(root, path) {
|
|
3943
6119
|
const p = path.replace(/^\.\/?/, "").replace(/\/+$/, "");
|
|
3944
6120
|
const near = (dir) => {
|
|
3945
|
-
const rel =
|
|
6121
|
+
const rel = relative2(root, dir).split(sep2).join("/");
|
|
3946
6122
|
if (rel.startsWith("..") || isAbsolute(rel))
|
|
3947
6123
|
return true;
|
|
3948
6124
|
return p === "" || p === rel || p.startsWith(rel + "/") || rel.startsWith(p + "/");
|
|
3949
6125
|
};
|
|
3950
6126
|
return near(catalogDirOf(root)) || near(orgPackDirOf(root));
|
|
3951
6127
|
}
|
|
6128
|
+
function extendUiRoutes(r, ctx, deps = {}, o = {}) {
|
|
6129
|
+
const known = uiKnowRoutes(uiRunRoutes(uiRoutes(r, ctx, deps), ctx, deps), ctx, {
|
|
6130
|
+
warm: o.warm === true
|
|
6131
|
+
});
|
|
6132
|
+
const base = uiInitiativeRoutes(known, ctx);
|
|
6133
|
+
const views = uiGraphRoutes(uiAcrossRoutes(uiMeRoutes(base, ctx), ctx), ctx);
|
|
6134
|
+
const specs = uiSpecRoutes(uiOnboardingRoutes(uiQuestionRoutes(views, ctx), ctx), ctx);
|
|
6135
|
+
const all = uiComponentTypeRoutes(uiLinkMatchRoutes(uiSketchRoutes(uiDeclaredRoutes(uiOwnerRoutes(uiLinkRoutes(uiCopilotRunRoutes(uiStageRoutes(uiContractsRoutes(uiOutsideRoutes(uiAuditRoutes(uiFactsRoutes(uiTrustRoutes(uiCockpitRoutes(uiRepoRoutes(uiDocumentRoutes(uiAiRoutes(uiHistoryRoutes(uiModelRunRoutes(uiKnowledgeRoutes(uiCiteRoutes(specs, ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx);
|
|
6136
|
+
return uiDecisionRoutes(all, ctx);
|
|
6137
|
+
}
|
|
3952
6138
|
async function startUi(root, o = {}, deps = {}) {
|
|
3953
6139
|
const srv = await startServer({
|
|
3954
6140
|
root,
|
|
@@ -3971,17 +6157,10 @@ async function startUi(root, o = {}, deps = {}) {
|
|
|
3971
6157
|
log.debug("request", fields);
|
|
3972
6158
|
};
|
|
3973
6159
|
})(),
|
|
3974
|
-
extend: (r, ctx) => {
|
|
3975
|
-
const base = uiKnowRoutes(uiRunRoutes(uiRoutes(r, ctx, deps), ctx, deps), ctx, {
|
|
3976
|
-
warm: o.warm === true
|
|
3977
|
-
});
|
|
3978
|
-
const views = uiGraphRoutes(uiAcrossRoutes(uiMeRoutes(base, ctx), ctx), ctx);
|
|
3979
|
-
const specs = uiSpecRoutes(uiOnboardingRoutes(uiQuestionRoutes(views, ctx), ctx), ctx);
|
|
3980
|
-
return uiStageRoutes(uiFactsRoutes(uiTrustRoutes(uiCockpitRoutes(uiRepoRoutes(uiDocumentRoutes(uiAiRoutes(uiHistoryRoutes(uiModelRunRoutes(uiKnowledgeRoutes(uiCiteRoutes(specs, ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx);
|
|
3981
|
-
}
|
|
6160
|
+
extend: (r, ctx) => extendUiRoutes(r, ctx, deps, { warm: o.warm === true })
|
|
3982
6161
|
});
|
|
3983
6162
|
keepCatalogsInMemory(true);
|
|
3984
|
-
void readCatalog(
|
|
6163
|
+
void readCatalog(join14(catalogDirOf(root), "generated"), { cacheRoot: root }).catch(() => void 0);
|
|
3985
6164
|
const stop = srv.events.listen((e) => {
|
|
3986
6165
|
const changed = e.type === "file.changed" && (typeof e.path !== "string" || catalogMayHaveChanged(root, e.path));
|
|
3987
6166
|
if (changed || e.type === "index.rebuilt")
|
|
@@ -4005,7 +6184,7 @@ function openBrowser(url) {
|
|
|
4005
6184
|
}
|
|
4006
6185
|
}
|
|
4007
6186
|
function untilStopped(embedded, stdin = process.stdin, o = {}) {
|
|
4008
|
-
return new Promise((
|
|
6187
|
+
return new Promise((resolve2) => {
|
|
4009
6188
|
const alive = o.alive ?? ((pid) => {
|
|
4010
6189
|
try {
|
|
4011
6190
|
process.kill(pid, 0);
|
|
@@ -4028,7 +6207,7 @@ function untilStopped(embedded, stdin = process.stdin, o = {}) {
|
|
|
4028
6207
|
stdin.off("close", stop);
|
|
4029
6208
|
stdin.pause();
|
|
4030
6209
|
}
|
|
4031
|
-
|
|
6210
|
+
resolve2();
|
|
4032
6211
|
};
|
|
4033
6212
|
process.once("SIGINT", stop);
|
|
4034
6213
|
process.once("SIGTERM", stop);
|
|
@@ -4074,12 +6253,14 @@ async function uiCommand(root, o, io) {
|
|
|
4074
6253
|
if (o.open)
|
|
4075
6254
|
openBrowser(srv.url);
|
|
4076
6255
|
await untilStopped(o.embed === "vscode", process.stdin, o.parent !== void 0 ? { parent: o.parent } : {});
|
|
6256
|
+
stopAllBuilds();
|
|
4077
6257
|
await srv.close();
|
|
4078
6258
|
io.out("Stopped.");
|
|
4079
6259
|
return 0;
|
|
4080
6260
|
}
|
|
4081
6261
|
export {
|
|
4082
6262
|
catalogMayHaveChanged,
|
|
6263
|
+
extendUiRoutes,
|
|
4083
6264
|
startUi,
|
|
4084
6265
|
startedLines,
|
|
4085
6266
|
uiCommand,
|