ryco-cli 0.1.29 → 0.1.31
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/README.md +37 -0
- package/dist/{AcpAdapter-8CSe4HK7.cjs → AcpAdapter-ByUp9-wk.cjs} +74 -54
- package/dist/{AcpAdapter-CG1wRWcf.mjs → AcpAdapter-CY_ANTKu.mjs} +74 -54
- package/dist/{AcpNativeLogging-B6TtnnKL.mjs → AcpNativeLogging-C7PC6imv.mjs} +30 -4
- package/dist/{AcpNativeLogging-DbjnRnTf.cjs → AcpNativeLogging-OQeAya9d.cjs} +33 -1
- package/dist/{AcpRegistryDriver-16ZTxUs3.cjs → AcpRegistryDriver-CD9GN7rI.cjs} +3 -3
- package/dist/{AcpRegistryDriver-DZFasKaz.mjs → AcpRegistryDriver-DMZ5VBWJ.mjs} +4 -4
- package/dist/AzureDevOpsSourceControlProvider-5giVYoNi.mjs +2053 -0
- package/dist/AzureDevOpsSourceControlProvider-BKMXEUaN.cjs +2055 -0
- package/dist/{BitbucketSourceControlProvider-DplKc8Wo.mjs → BitbucketSourceControlProvider-BVUBsEUF.mjs} +63 -23
- package/dist/{BitbucketSourceControlProvider-B-ZwKj72.cjs → BitbucketSourceControlProvider-BWnnrbch.cjs} +62 -22
- package/dist/{BunPTY-Dcl1Lp62.mjs → BunPTY-CMbDQ1Xb.mjs} +1 -1
- package/dist/{ClaudeDriver-r0lJ9vmE.cjs → ClaudeDriver-DCrqouOR.cjs} +1376 -194
- package/dist/{ClaudeDriver-0gGVop27.mjs → ClaudeDriver-DVOss8cC.mjs} +1377 -195
- package/dist/{CodexDriver-bJAbhSwY.cjs → CodexDriver-CBQPHJqU.cjs} +259 -29
- package/dist/{CodexDriver-baT02xVb.mjs → CodexDriver-DJoTky7Z.mjs} +261 -31
- package/dist/{CodexTextGeneration-Dwp6X3dq.mjs → CodexTextGeneration-BSgNgfZv.mjs} +6 -6
- package/dist/{CodexTextGeneration-DFZ5y4gj.cjs → CodexTextGeneration-C9MDQmZm.cjs} +7 -7
- package/dist/{CopilotDriver-C0OhnSB1.mjs → CopilotDriver-CUkdPXcH.mjs} +78 -20
- package/dist/{CopilotDriver-B_jEqfYQ.cjs → CopilotDriver-DbaMDH16.cjs} +76 -18
- package/dist/{CursorDriver-DinXkGP9.mjs → CursorDriver-B-ZilXJB.mjs} +115 -98
- package/dist/{CursorDriver-BBFEW7ZI.cjs → CursorDriver-DKQqPbKH.cjs} +116 -99
- package/dist/{ForgejoSourceControlProvider-iG9o7aZB.cjs → ForgejoSourceControlProvider-CgpjFECj.cjs} +94 -60
- package/dist/{ForgejoSourceControlProvider-Dr0fxfud.mjs → ForgejoSourceControlProvider-DemMYUye.mjs} +95 -61
- package/dist/{GitHubSourceControlProvider-L1yFpep4.cjs → GitHubSourceControlProvider-C4un6hOK.cjs} +218 -119
- package/dist/{GitHubSourceControlProvider-CnIu0n2Y.mjs → GitHubSourceControlProvider-CRSDYcjc.mjs} +206 -108
- package/dist/GitLabSourceControlProvider-D8W1ZLPh.cjs +2260 -0
- package/dist/GitLabSourceControlProvider-DpWkJ0M3.mjs +2258 -0
- package/dist/{GrokDriver-D_Ylzh5z.cjs → GrokDriver-BQ6G_7ZZ.cjs} +11 -12
- package/dist/{GrokDriver-pX-N6HGS.mjs → GrokDriver-Buvas9-q.mjs} +7 -8
- package/dist/{Metrics-BjqGlIP4.mjs → Metrics-C3vLS9rz.mjs} +1264 -1151
- package/dist/{Metrics-Bf96NJ4j.cjs → Metrics-DjRPLE98.cjs} +1295 -1134
- package/dist/{NodePTY-D7tFWuQe.mjs → NodePTY-DgnuBruq.mjs} +1 -1
- package/dist/{NodeSqliteClient-BGg70C7Q.cjs → NodeSqliteClient-D8bzGgPN.cjs} +1 -1
- package/dist/{OpenCodeDriver-Byu1pf_i.mjs → OpenCodeDriver-BWEZD841.mjs} +184 -50
- package/dist/{OpenCodeDriver-BFEw5t09.cjs → OpenCodeDriver-qUiX0RLH.cjs} +185 -51
- package/dist/{SideQuestionIsolation-81IoAuoi.mjs → SideQuestionIsolation-B1T-DsR0.mjs} +1 -1
- package/dist/{SideQuestionIsolation-CMPkCUP0.cjs → SideQuestionIsolation-BPC-Bxsz.cjs} +1 -1
- package/dist/bin.cjs +50689 -15659
- package/dist/bin.mjs +50578 -15585
- package/dist/client/assets/AccountSettings-B88Q_w9Y.js +1 -0
- package/dist/client/assets/AiFocusSettings-Bb0TrCPn.js +1 -0
- package/dist/client/assets/{AnimatedHeight-BNg7wnTK.js → AnimatedHeight-epXOsgdm.js} +1 -1
- package/dist/client/assets/AppearanceSettings-XS9WRVPU.js +1 -0
- package/dist/client/assets/AutomationsDialog-bZckh4Y3.js +1 -0
- package/dist/client/assets/AutomationsSummary-wPFZCF49.js +1 -0
- package/dist/client/assets/BranchToolbar.logic-DjzLKbbB.js +3 -0
- package/dist/client/assets/BrowserPanel-CdBLnnfz.js +1 -0
- package/dist/client/assets/ChatRightPanel-BCINd0vP.js +34 -0
- package/dist/client/assets/ChatThreadRouteView-BFZ0Si-l.js +1 -0
- package/dist/client/assets/ColorSwatchPicker-CGyo99o0.js +1 -0
- package/dist/client/assets/CommandPalette.logic-BIOMtnEQ.js +1 -0
- package/dist/client/assets/CommandPaletteDialog-Db4LH--z.js +2 -0
- package/dist/client/assets/CompositeRoot-DtnwMJKj.js +1 -0
- package/dist/client/assets/ComputerUseSettings-CkqCqDsU.js +1 -0
- package/dist/client/assets/ConnectionsSettings-B9mxExdG.js +7 -0
- package/dist/client/assets/CreatePullRequestDialog-CJ7pNCue.js +1 -0
- package/dist/client/assets/CreatePullRequestDialogHost-n4ZjdSr4.js +1 -0
- package/dist/client/assets/DiagnosticsSettings-f9rFG9re.js +2 -0
- package/dist/client/assets/DiagnosticsSettings.logic-Byf-Bu4S.js +1 -0
- package/dist/client/assets/DialogHandle-C0c_rK3I.js +1 -0
- package/dist/client/assets/{DialogPopup-DdZlhHuQ.js → DialogPopup-BKW1PEEa.js} +1 -1
- package/dist/client/assets/DialogRoot-BIubqrv0.js +1 -0
- package/dist/client/assets/DialogTitle-BAX_FLoG.js +1 -0
- package/dist/client/assets/{DialogViewport-BwuqbGLY.js → DialogViewport-C2iOUx0O.js} +1 -1
- package/dist/client/assets/DiffPanel-BkqDt_SS.js +25 -0
- package/dist/client/assets/DisclosureRegion-B8556yU0.js +1 -0
- package/dist/client/assets/DraftChatThreadRouteView-BKgRKJ2w.js +1 -0
- package/dist/client/assets/{ExternalIdentityWeb.logic-DsTJQkg-.js → ExternalIdentityWeb.logic-Cu6suNrN.js} +1 -1
- package/dist/client/assets/GlassSurface-DLYYzRdP.js +1 -0
- package/dist/client/assets/HostedConnectionControls-CoVqtnrK.js +1 -0
- package/dist/client/assets/HostedHubRoot-CM2CwPUr.js +2 -0
- package/dist/client/assets/{HostedNativeAuthorizationRoute-BSq_AlY2.js → HostedNativeAuthorizationRoute-Bizmqrh_.js} +2 -2
- package/dist/client/assets/HostedNodeRenameDialog-SXWMsTO0.js +1 -0
- package/dist/client/assets/HostedRelayTrustNotice-BfTyrBi5.js +1 -0
- package/dist/client/assets/InboxStatusGlyph-BPTqzp7l.js +1 -0
- package/dist/client/assets/IntegrationsSettingsPanel-DAPvkV4i.js +1 -0
- package/dist/client/assets/KeybindingsSettings-B8g-YI2O.js +1 -0
- package/dist/client/assets/LazyBrowserPanel-Ds9cdOC_.js +2 -0
- package/dist/client/assets/McpServersSettings-Dw6G3ff3.js +6 -0
- package/dist/client/assets/MobileSheet-DfgXNWbd.js +1 -0
- package/dist/client/assets/ModelTuningDial-DQBAbuNL.js +1 -0
- package/dist/client/assets/NewWorktreeDialog-CDZAJmK6.js +4 -0
- package/dist/client/assets/NodeSecuritySettings-3MELA9ru.js +1 -0
- package/dist/client/assets/NullStore-gT_6_xZE.js +1 -0
- package/dist/client/assets/OnboardingDialog-CAAtNnJf.js +1 -0
- package/dist/client/assets/OpinionatedPluginsSettings-AiDB0Vl4.js +1 -0
- package/dist/client/assets/{PdfAttachmentPreview-CTo1AyZs.js → PdfAttachmentPreview-BFNW2wjN.js} +2 -2
- package/dist/client/assets/{PhoneGetAppInterstitial-DwopOWnQ.js → PhoneGetAppInterstitial-D8fwZ8E9.js} +1 -1
- package/dist/client/assets/PhoneHome-YHgHljA0.js +1 -0
- package/dist/client/assets/PhoneSettingsSurface-BVxJppvK.js +2 -0
- package/dist/client/assets/PreviewPanel-BYcEnkv9.js +643 -0
- package/dist/client/assets/ProjectScriptDialog-K34K2Kzm.js +1 -0
- package/dist/client/assets/ProjectsPage-DXnMYLjb.js +1 -0
- package/dist/client/assets/PromoteChatDialog-CAY7_wJ-.js +1 -0
- package/dist/client/assets/ProviderLimitWindow-CK04BSkM.js +1 -0
- package/dist/client/assets/ProviderUpdateLaunchNotification.logic-BMBrDDOC.js +1 -0
- package/dist/client/assets/ProvidersSettingsPanel-C85PX9Es.js +1 -0
- package/dist/client/assets/PullRequestsPage-C0rgfGwB.js +1 -0
- package/dist/client/assets/{QuitShortcutFeedback-eCvP_MNj.js → QuitShortcutFeedback-DSkKqETs.js} +1 -1
- package/dist/client/assets/RedactedSensitiveText-DQ0YveQd.js +1 -0
- package/dist/client/assets/RollingText-BzvpP5fa.js +1 -0
- package/dist/client/assets/RootAppShell-BYQ-eGNT.js +6 -0
- package/dist/client/assets/{Separator-BiINUUQl.js → Separator-DpiW19Vw.js} +1 -1
- package/dist/client/assets/SettingsPage-BCFN7Ju2.js +2 -0
- package/dist/client/assets/SettingsPanels-CP6z2b1x.js +3 -0
- package/dist/client/assets/SettingsPanels.editor-D8U-TWWG.js +1 -0
- package/dist/client/assets/SettingsSelect-DEhNaz3E.js +1 -0
- package/dist/client/assets/Sidebar.logic-D_ODfSpl.js +1 -0
- package/dist/client/assets/SimulatorPanel-DCT6fZ0X.js +1 -0
- package/dist/client/assets/SourceControlSettings-DxUtvPMK.js +1 -0
- package/dist/client/assets/StatisticsPage-C9SrwtM4.js +1 -0
- package/dist/client/assets/StatisticsPanel-BMYLETs0.js +1 -0
- package/dist/client/assets/ThreadLinkList-BN5z5EpS.js +169 -0
- package/dist/client/assets/ThreadTerminalDrawer-CgIFOlR-.js +18 -0
- package/dist/client/assets/ThreadWorkspacePanel-DvXoqQPF.js +3 -0
- package/dist/client/assets/ToolbarRootContext-8FsV9Cpj.js +1 -0
- package/dist/client/assets/TraitsPicker-DcrpJahy.js +1 -0
- package/dist/client/assets/Virtualizer-CwXeUPla.js +1345 -0
- package/dist/client/assets/WorkspacePullRequestPanel-3mlcJn9G.js +1 -0
- package/dist/client/assets/WorkspaceReview-UGbCgXeK.js +2 -0
- package/dist/client/assets/{abnfDiagram-N423BO3Z-DAmGykdr.js → abnfDiagram-N423BO3Z-DVEM6Scc.js} +1 -1
- package/dist/client/assets/{activity-DRZlu-MC.js → activity-DVCuAejY.js} +1 -1
- package/dist/client/assets/{alert-CwrDisFU.js → alert-FV7ZIit_.js} +1 -1
- package/dist/client/assets/alert-dialog-CgXR9tED.js +1 -0
- package/dist/client/assets/app-window-3xtwqZgI.js +1 -0
- package/dist/client/assets/appChrome-COCz-T7w.js +1 -0
- package/dist/client/assets/{appearancePreferences-nFx4MHoI.js → appearancePreferences-NnsvXwBe.js} +1 -1
- package/dist/client/assets/architecture-7GRP2DOG-DXXb_0_g.js +1 -0
- package/dist/client/assets/{architectureDiagram-T3A2C74G-DypU2ERW.js → architectureDiagram-T3A2C74G-B6pcKiCj.js} +1 -1
- package/dist/client/assets/{arrow-left-XhcJWpRy.js → arrow-left-Blu3LSE2.js} +1 -1
- package/dist/client/assets/{arrow-right-BBs6iTUL.js → arrow-right-CO3CH0ne.js} +1 -1
- package/dist/client/assets/arrow-up-B69gV2Bq.js +1 -0
- package/dist/client/assets/{arrow-up-right-CJ9Vq7nK.js → arrow-up-right-BbPGFOLA.js} +1 -1
- package/dist/client/assets/authorization-Ob6YwWiI.js +1 -0
- package/dist/client/assets/autocomplete-RVpjsrDv.js +1 -0
- package/dist/client/assets/ban-BIm2PVkE.js +1 -0
- package/dist/client/assets/{blockDiagram-VBNYF7ZC-B9MhR9hY.js → blockDiagram-VBNYF7ZC-WNerGp9V.js} +1 -1
- package/dist/client/assets/{bot-D5wgL7pu.js → bot-C9nWtaWq.js} +1 -1
- package/dist/client/assets/browserState-jt47YsqD.js +1 -0
- package/dist/client/assets/{c4Diagram-5PPSVZJV-C22FWSoE.js → c4Diagram-5PPSVZJV-wp1ioOoM.js} +1 -1
- package/dist/client/assets/camera-C13VoW3c.js +1 -0
- package/dist/client/assets/capabilities-CfOhq009.js +1 -0
- package/dist/client/assets/channel-56r2qL2v.js +1 -0
- package/dist/client/assets/{chart-column-D6Lnkklk.js → chart-column-DRv0YtL_.js} +1 -1
- package/dist/client/assets/checkbox-CIIIY9FA.js +1 -0
- package/dist/client/assets/checks-Dw022Bvy.js +1 -0
- package/dist/client/assets/{chunk-2GRJ4B5K-D1tZuCzA.js → chunk-2GRJ4B5K-DRl3ILpc.js} +1 -1
- package/dist/client/assets/{chunk-4I5QYGJK-Clf86zK0.js → chunk-4I5QYGJK-CYTRl4bo.js} +1 -1
- package/dist/client/assets/{chunk-5RXB4S5H-B36N5RYZ.js → chunk-5RXB4S5H-VCNwRsOa.js} +1 -1
- package/dist/client/assets/{chunk-6Q2QTUOP-IAxdzsQJ.js → chunk-6Q2QTUOP-DnSeKGPG.js} +1 -1
- package/dist/client/assets/{chunk-7Z6QIM7H-Ctqebv_4.js → chunk-7Z6QIM7H-Cjilh9TF.js} +1 -1
- package/dist/client/assets/{chunk-GF5L2VYU-CAfciJae.js → chunk-GF5L2VYU-CGR7AW0t.js} +1 -1
- package/dist/client/assets/{chunk-I66GZJ75-BZ7XtZf4.js → chunk-I66GZJ75-CXSn8dDu.js} +1 -1
- package/dist/client/assets/{chunk-JQJVKLGR-DVuZCWnv.js → chunk-JQJVKLGR-Dro-75lU.js} +1 -1
- package/dist/client/assets/{chunk-KBJHAD2P-DtWbzsRm.js → chunk-KBJHAD2P-BDqN2kVf.js} +1 -1
- package/dist/client/assets/{chunk-NSK5VX7P-BVMz_Hc4.js → chunk-NSK5VX7P-DGo5UczH.js} +1 -1
- package/dist/client/assets/{chunk-QR6OTTB3-DWitTCy-.js → chunk-QR6OTTB3-RWuyWnBI.js} +1 -1
- package/dist/client/assets/{chunk-UBXNYLIW-BuxXXP2s.js → chunk-UBXNYLIW-BPRzeB0w.js} +1 -1
- package/dist/client/assets/{chunk-W5SLKNZC-Bnf3sQl4.js → chunk-W5SLKNZC-DYWocTFi.js} +1 -1
- package/dist/client/assets/{chunk-WRU74C26-xu-KDOon.js → chunk-WRU74C26-ChbB1jY3.js} +1 -1
- package/dist/client/assets/circle-stop-D8B4hRpY.js +1 -0
- package/dist/client/assets/circle-x-CNLfrTqR.js +1 -0
- package/dist/client/assets/classDiagram-JCYQIIEL-DGvmFev3.js +1 -0
- package/dist/client/assets/classDiagram-v2-OCEON4UE-DGvmFev3.js +1 -0
- package/dist/client/assets/{clipboard-copy-oDSkUUg4.js → clipboard-copy-DGi8FKNO.js} +1 -1
- package/dist/client/assets/clock-3-DmJ4_XPI.js +1 -0
- package/dist/client/assets/clock-DZIuPtNG.js +1 -0
- package/dist/client/assets/color-picker-BdBXje-a.js +1 -0
- package/dist/client/assets/columns-2-CWlMOClK.js +1 -0
- package/dist/client/assets/combobox-Dtjtf7AB.js +1 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-DGNyiNSf.js → cynefin-OW5HDTMX-TuShvMuM.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-MW4NZA55-_ctibv4i.js → cynefinDiagram-MW4NZA55-B0Edt2Em.js} +1 -1
- package/dist/client/assets/{dagre-VZM6K2ZE-Bo-bS1Y0.js → dagre-VZM6K2ZE-D-eiuqHx.js} +1 -1
- package/dist/client/assets/{data-list-DLsM39Hp.js → data-list-BAx6jBY1.js} +1 -1
- package/dist/client/assets/desktopUpdateAtoms-BjrLa3tl.js +1 -0
- package/dist/client/assets/desktopWorkspace-C2wXVBhc.js +1 -0
- package/dist/client/assets/{deviceName-z0ZWSq0z.js → deviceName-B4youG0v.js} +1 -1
- package/dist/client/assets/{diagram-7IWD3JNH-DSjtw1SY.js → diagram-7IWD3JNH-DgM6lMlR.js} +1 -1
- package/dist/client/assets/{diagram-B4RE2ZJO-B7fWEvIa.js → diagram-B4RE2ZJO-CyHNDjzV.js} +1 -1
- package/dist/client/assets/{diagram-LBJQPF4R-trRYCE5d.js → diagram-LBJQPF4R-DDzuLIN5.js} +1 -1
- package/dist/client/assets/{diagram-Q27KOJAE-_DYcRggk.js → diagram-Q27KOJAE-BaBvK9QB.js} +1 -1
- package/dist/client/assets/{diagram-UB23O5K3-BkMzOHX8.js → diagram-UB23O5K3-CzZ2ewuZ.js} +1 -1
- package/dist/client/assets/dialogWords-Cg0YkJVg.js +1 -0
- package/dist/client/assets/diffRouteSearch-D2MKN_ah.js +1 -0
- package/dist/client/assets/disclosureMotion-cbMJn5-z.js +1 -0
- package/dist/client/assets/{download-CHdS3R_v.js → download-TvtXS4hE.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-BXEA7PRR-DubiOMCG.js → ebnfDiagram-BXEA7PRR-CL1vdOKi.js} +1 -1
- package/dist/client/assets/editorPreferences-DqKyA-2Z.js +1 -0
- package/dist/client/assets/ellipsis-CDK1dNFZ.js +1 -0
- package/dist/client/assets/ellipsis-vertical-CwzEUTwV.js +1 -0
- package/dist/client/assets/{empty-BlBl_Vvw.js → empty-BovKoxbn.js} +1 -1
- package/dist/client/assets/{erDiagram-JOGREHBK-CFDiNfSJ.js → erDiagram-JOGREHBK-DmQR2if6.js} +1 -1
- package/dist/client/assets/eventmodeling-NTZA5JFV-D272GIcY.js +1 -0
- package/dist/client/assets/external-link-CJeVJZ7s.js +1 -0
- package/dist/client/assets/eye-BUmrXqzg.js +1 -0
- package/dist/client/assets/file-diff-BWc3mYsz.js +1 -0
- package/dist/client/assets/flowDiagram-UKHOOZJN-DLyS38Mm.js +1 -0
- package/dist/client/assets/folder-git-2-CPxg5wKP.js +1 -0
- package/dist/client/assets/folder-open-DcMymvp1.js +1 -0
- package/dist/client/assets/formatCount-B4KLI651.js +1 -0
- package/dist/client/assets/{ganttDiagram-PKOTCBZU-DT2XhE-m.js → ganttDiagram-PKOTCBZU-1q2nUL92.js} +1 -1
- package/dist/client/assets/git-pull-request-BFPwpm3c.js +1 -0
- package/dist/client/assets/{gitGraph-4MIJSDKK-BI5UGiqb.js → gitGraph-4MIJSDKK-COtwmv2S.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-CzAB97Rd.js → gitGraphDiagram-DS77QQ5N-ChsvaxX4.js} +1 -1
- package/dist/client/assets/{group-DZJuSiJL.js → group-DWWn0dTx.js} +1 -1
- package/dist/client/assets/hammer-BXo-EU88.js +1 -0
- package/dist/client/assets/hostedConnectionCoordinator-BkAE-RhA.js +1 -0
- package/dist/client/assets/hostedPairing-5n5srRC9.js +1 -0
- package/dist/client/assets/{index-BAJdq3TK.js → index-DARIYokI.js} +2 -2
- package/dist/client/assets/{info-A6RAGUB7-DdGYYgLX.js → info-A6RAGUB7-CI2qZ9wW.js} +1 -1
- package/dist/client/assets/{infoDiagram-6WML65LV-zhUpER6a.js → infoDiagram-6WML65LV-BPmgcTPH.js} +1 -1
- package/dist/client/assets/{input-CzU08wbL.js → input-CCie4mm1.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-BQLkqykE.js → ishikawaDiagram-WSZJBQD7-LyS9p3Yg.js} +1 -1
- package/dist/client/assets/{journeyDiagram-NVQOT4AX-DBgsbKOF.js → journeyDiagram-NVQOT4AX-BYaAmN65.js} +1 -1
- package/dist/client/assets/{kanban-definition-27J2QSJJ-DF-Rr-xs.js → kanban-definition-27J2QSJJ-D-pwzqFt.js} +1 -1
- package/dist/client/assets/{key-round-CvyrSjb2.js → key-round-BlrUTfxB.js} +1 -1
- package/dist/client/assets/keyboard-DlBdq6sz.js +1 -0
- package/dist/client/assets/label-ChtVI9lp.js +1 -0
- package/dist/client/assets/layers-BB-ruGvd.js +1 -0
- package/dist/client/assets/lifecycleSuggestionPolicy-0ERe_O53.js +1 -0
- package/dist/client/assets/list-checks-BkM_ZRsj.js +1 -0
- package/dist/client/assets/loader-3vQnfqIT.js +1 -0
- package/dist/client/assets/{log-out-D3rICSt5.js → log-out-CKq9YiLY.js} +1 -1
- package/dist/client/assets/{logging-C6tXZlGQ.js → logging-CCg76rgn.js} +1 -1
- package/dist/client/assets/main-Ch0bwS-M.js +9 -0
- package/dist/client/assets/main-Di0isH9D.css +2 -0
- package/dist/client/assets/{mermaid-parser.core-BiGKtilP.js → mermaid-parser.core-RK9ah5RM.js} +2 -2
- package/dist/client/assets/{mermaidEngine-Cjfgf3Bg.js → mermaidEngine-Dr8UsPhH.js} +3 -3
- package/dist/client/assets/message-circle-dashed-BrjwqS7-.js +1 -0
- package/dist/client/assets/messageQueueDrain-B7R7Qmq6.js +1 -0
- package/dist/client/assets/messages-square-cDTkDJv_.js +1 -0
- package/dist/client/assets/{mindmap-definition-FAOFIHXS-SiU9JwWJ.js → mindmap-definition-FAOFIHXS-DVudWasx.js} +1 -1
- package/dist/client/assets/{packet-AYTQ26CC-F-HuW5Dr.js → packet-AYTQ26CC-DnkTsVhD.js} +1 -1
- package/dist/client/assets/{pairingUrl-rQM9kSGT.js → pairingUrl-Dn1e2CVw.js} +1 -1
- package/dist/client/assets/panels-top-left-B7jZiLth.js +1 -0
- package/dist/client/assets/path-text-3Rfu6lt8.js +1 -0
- package/dist/client/assets/{pegDiagram-VL7TDLO6-cC-QUd7I.js → pegDiagram-VL7TDLO6-Do5PIp8B.js} +1 -1
- package/dist/client/assets/pencil-Bzg-nc6d.js +1 -0
- package/dist/client/assets/{pie-WAS4IAKB-BI3XUcLN.js → pie-WAS4IAKB-B_nNQOzn.js} +1 -1
- package/dist/client/assets/{pieDiagram-7S7Q4E2Y-BP4gJ4ZK.js → pieDiagram-7S7Q4E2Y-D_IG4xwA.js} +1 -1
- package/dist/client/assets/{play-BJZrOx6T.js → play-DpXXQ1qB.js} +1 -1
- package/dist/client/assets/popover-OWEv6cQz.js +48 -0
- package/dist/client/assets/presentationTier-DPVAxTK4.js +1 -0
- package/dist/client/assets/previewRouteSearch-BjZdTuSf.js +1 -0
- package/dist/client/assets/primary-Cf-xJExj.js +4 -0
- package/dist/client/assets/primitives-CxTBN7D4.js +1 -0
- package/dist/client/assets/projectCheckouts.logic-DUT4AxXr.js +1 -0
- package/dist/client/assets/projectPaths-B8aVmOZX.js +1 -0
- package/dist/client/assets/projectPreferences-CzWBItFj.js +1 -0
- package/dist/client/assets/providerCapabilities-B2YOPsLN.js +1 -0
- package/dist/client/assets/providerDriverMeta-BHHL9uw0.js +1 -0
- package/dist/client/assets/providerModels-v9PYbzRE.js +1 -0
- package/dist/client/assets/providerStatus-CDPaqb0S.js +1 -0
- package/dist/client/assets/pullRequestsModel.logic-Dv_om_C4.js +1 -0
- package/dist/client/assets/{qr-code-BFO8lV1M.js → qr-code-BlbiBIwH.js} +1 -1
- package/dist/client/assets/{qr-code-Cmfz5JKf.js → qr-code-DywzVgQC.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-DO-N1m-q.js → quadrantDiagram-CIZ2JOQS-DY6fJyrM.js} +1 -1
- package/dist/client/assets/{radar-RG4KPBEZ-CfayXQ5F.js → radar-RG4KPBEZ-D9689I5m.js} +1 -1
- package/dist/client/assets/radio-group-DVwD6Cuy.js +1 -0
- package/dist/client/assets/{railroad-74A4TZTK-CT6f-etT.js → railroad-74A4TZTK-DVzWbQnl.js} +1 -1
- package/dist/client/assets/railroad-abnf-HS5TGJTU-C_2P6umA.js +1 -0
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-aGlR3R3z.js +1 -0
- package/dist/client/assets/railroad-peg-WCYAUIDC-DCB9oE8y.js +1 -0
- package/dist/client/assets/{railroadDiagram-AXF67PYL-DC8egbob.js → railroadDiagram-AXF67PYL-D1oJ6Jtl.js} +1 -1
- package/dist/client/assets/readCache-B7cDPz2O.js +1 -0
- package/dist/client/assets/{refresh-cw-K_hU8_ld.js → refresh-cw-CRUTivd6.js} +1 -1
- package/dist/client/assets/{requirementDiagram-LRYGKXZP-DJMzcLk_.js → requirementDiagram-LRYGKXZP-7RM8iREp.js} +1 -1
- package/dist/client/assets/rotate-ccw-BE2ZCwL6.js +1 -0
- package/dist/client/assets/roving-radio-group-NMPWrDqe.js +1 -0
- package/dist/client/assets/rows-2-DDS4dGTX.js +1 -0
- package/dist/client/assets/runtime-DjjjAcTE.js +18 -0
- package/dist/client/assets/safePolygon-C1AzMRS4.js +1 -0
- package/dist/client/assets/{sankeyDiagram-W5VNT64P-YR6aIYLj.js → sankeyDiagram-W5VNT64P-C1tKAPGN.js} +1 -1
- package/dist/client/assets/{save-Mh-PA_2P.js → save-OZdkPs_y.js} +1 -1
- package/dist/client/assets/scroll-area-80P7vbjw.js +1 -0
- package/dist/client/assets/{scrollEdges-Zcpuz3un.js → scrollEdges-C0Oc-5qi.js} +1 -1
- package/dist/client/assets/select-Cb9FUPHD.js +1 -0
- package/dist/client/assets/separator-CO8bC7Wc.js +1 -0
- package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BVeSqWDA.js → sequenceDiagram-SI44F4Z6-DGVpb7jL.js} +1 -1
- package/dist/client/assets/settings-2-PrRDu6Lc.js +1 -0
- package/dist/client/assets/settingsLayout-CRFNCk_D.js +1 -0
- package/dist/client/assets/sheet-C5DW7qVO.js +1 -0
- package/dist/client/assets/{shield-i098Bm_B.js → shield-CBHYqlkR.js} +1 -1
- package/dist/client/assets/{shield-alert-CXmR7fM9.js → shield-alert-Dc2cGwyR.js} +1 -1
- package/dist/client/assets/{shield-check-CdFhWT_o.js → shield-check-_wl7R4Js.js} +1 -1
- package/dist/client/assets/sidebar-0BBaFNm3.js +1 -0
- package/dist/client/assets/sidebarProjectFolders-BjWzbt8C.js +3 -0
- package/dist/client/assets/sidebarProjectGrouping-nQRStWrZ.js +1 -0
- package/dist/client/assets/sidebarStatusText-BBKuNies.js +1 -0
- package/dist/client/assets/sidebarUndo-Dfq1lvyF.js +1 -0
- package/dist/client/assets/{skeleton-BwFgUtuE.js → skeleton-DfUe6VV_.js} +1 -1
- package/dist/client/assets/sliders-horizontal-BGXkwvKv.js +1 -0
- package/dist/client/assets/sliding-tabs-BnDSjRU3.js +39 -0
- package/dist/client/assets/{smartphone-DGfizz51.js → smartphone-OEAQOTdv.js} +1 -1
- package/dist/client/assets/spinner-lHRJWsym.js +1 -0
- package/dist/client/assets/square-B05NKv3e.js +1 -0
- package/dist/client/assets/{star-J3x3fktG.js → star-C8mHmfZu.js} +1 -1
- package/dist/client/assets/{stateDiagram-OKZ733FA-DoIZN669.js → stateDiagram-OKZ733FA-CCOAegCG.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-UEYNNEHI-D60qqBed.js +1 -0
- package/dist/client/assets/storeSelectors-BichCKDC.js +1 -0
- package/dist/client/assets/surfaceMorph-BTRijmlU.js +1 -0
- package/dist/client/assets/{swimlanes-SLNWSIFB-BoFLigyK.js → swimlanes-SLNWSIFB-CFsLYT4h.js} +1 -1
- package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-D0SaJgSn.js +8 -0
- package/dist/client/assets/switch-oQc7Ifbl.js +1 -0
- package/dist/client/assets/text-wrap-DSRdSfDm.js +1 -0
- package/dist/client/assets/textarea-VyiIZ0wQ.js +1 -0
- package/dist/client/assets/threadPriorityRefreshRuntime-H656C1J_.js +1 -0
- package/dist/client/assets/ticker-CPutfhZ4.js +1 -0
- package/dist/client/assets/{timeline-definition-Z64GVDOM-D65EmNWc.js → timeline-definition-Z64GVDOM-B4agmO7R.js} +1 -1
- package/dist/client/assets/toggle-DFqtCPt-.js +1 -0
- package/dist/client/assets/toggle-group-C5ICmqkI.js +1 -0
- package/dist/client/assets/{tokenModePresentation-C0-NgmX5.js → tokenModePresentation-umDRm4k1.js} +1 -1
- package/dist/client/assets/tooltip-DbqhseXd.js +1 -0
- package/dist/client/assets/{treeView-Q6P3EWNA-C2S-OPgI.js → treeView-Q6P3EWNA-C_S3udFB.js} +1 -1
- package/dist/client/assets/{treemap-WGGIJYW6-6Ovsu9iH.js → treemap-WGGIJYW6-B-kUSAkE.js} +1 -1
- package/dist/client/assets/undo-2-lyluTGMW.js +1 -0
- package/dist/client/assets/{unplug-BEfcl9Ht.js → unplug-D6iu-qlh.js} +1 -1
- package/dist/client/assets/usage-ClF-eUmy.js +1 -0
- package/dist/client/assets/useActiveIndicator-R5NWu8Fg.js +1 -0
- package/dist/client/assets/useAppSidebarCollapsed-BbMxfio5.js +1 -0
- package/dist/client/assets/useAppearancePreference-Ix13pMKy.js +1 -0
- package/dist/client/assets/useAtlassian-BYDCc9lv.js +0 -0
- package/dist/client/assets/useCheckoutResolution-CR-oOp5-.js +1 -0
- package/dist/client/assets/useDesktopRelaunchGuard-Jmh615iZ.js +1 -0
- package/dist/client/assets/useDiffFileNavigation-DeuzdD5E.js +15 -0
- package/dist/client/assets/useDiffLayout-DhcXwhBL.js +1 -0
- package/dist/client/assets/useEnvironmentSettingsTarget-DM6BRDT7.js +1 -0
- package/dist/client/assets/useFocus-CV0KIZwg.js +1 -0
- package/dist/client/assets/useGit-CUcs7h5_.js +1 -0
- package/dist/client/assets/useGitBranchSearch-CeKFtnCL.js +1 -0
- package/dist/client/assets/useHostedDraftExecutionTarget-DvQU5UOq.js +1 -0
- package/dist/client/assets/useLabelableId-CEgCRi6q.js +1 -0
- package/dist/client/assets/useLandingFlash-BfOOlFtW.js +1 -0
- package/dist/client/assets/useLogicalProjectSnapshots-CRNZke74.js +1 -0
- package/dist/client/assets/useNavigate-sQZthdyT.js +1 -0
- package/dist/client/assets/usePageLeadingInsetClass-Bf4nUvx4.js +1 -0
- package/dist/client/assets/usePhoneThreadActions-VaQ-mZex.js +1 -0
- package/dist/client/assets/usePopupHandleStore-DimKo2Yy.js +1 -0
- package/dist/client/assets/usePopupViewport-YeqX_UM3.js +1 -0
- package/dist/client/assets/usePresentationTier-B9kCQ4YH.js +1 -0
- package/dist/client/assets/useProject-Kt74Z12J.js +1 -0
- package/dist/client/assets/useProviderOptionsUpdater-CTzNh2B7.js +2 -0
- package/dist/client/assets/usePullRequestFilesViewed-D5Snmzys.js +1 -0
- package/dist/client/assets/usePullRequestsModel-DJL5HHbP.js +40 -0
- package/dist/client/assets/useScrollLock-CLZ31xsK.js +1 -0
- package/dist/client/assets/useSidebarThreadActions-n6EkLATf.js +6 -0
- package/dist/client/assets/{useStatistics-D55-xLMz.js → useStatistics-DD0mYS4J.js} +14 -14
- package/dist/client/assets/{useTheme-B441xg4F.js → useTheme-Bdy-BDs-.js} +1 -1
- package/dist/client/assets/useValueChanged-DoumqMnV.js +1 -0
- package/dist/client/assets/{user-round-C-U7TZKG.js → user-round-BPDXr8Gq.js} +1 -1
- package/dist/client/assets/{vennDiagram-T6HMQDX7-fZAexcYr.js → vennDiagram-T6HMQDX7-B1UEn_j_.js} +1 -1
- package/dist/client/assets/{vscode-icons-BYX6fBsM.js → vscode-icons-Ccy-gzFN.js} +1 -1
- package/dist/client/assets/{wardley-WFR3VGLG-gdYWSMxK.js → wardley-WFR3VGLG-gPdyDc-7.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-T6FBY63Y-iDJxQb70.js → wardleyDiagram-T6FBY63Y-2aDcwOA4.js} +1 -1
- package/dist/client/assets/workspaceLifecycle-OJ1aKksw.js +3 -0
- package/dist/client/assets/workspaceMetadataProjection-CyEyJhUa.js +1 -0
- package/dist/client/assets/workspaceRouteSearch-CCavUdbH.js +1 -0
- package/dist/client/assets/{wrench-D79uBUSl.js → wrench-CPUCA_rn.js} +1 -1
- package/dist/client/assets/wsTransport-C88seBPA.js +15 -0
- package/dist/client/assets/{xychartDiagram-ELKLHX3M-B4cfMwpJ.js → xychartDiagram-ELKLHX3M-C1_7Q-1i.js} +1 -1
- package/dist/client/favicon.svg +1 -1
- package/dist/client/index.html +1 -1
- package/dist/collectUint8StreamText-BsLSgI8i.mjs +710 -0
- package/dist/collectUint8StreamText-D5olwfpM.cjs +757 -0
- package/dist/{deviceHostWorker-L8C9wy38.mjs → deviceHostWorker-B-Dn-6An.mjs} +1 -1
- package/dist/{deviceHostWorker-CYnrwfrv.cjs → deviceHostWorker-D4HauWIV.cjs} +1 -1
- package/dist/{makeManagedServerProvider-CUwxc0Pn.cjs → makeManagedServerProvider-B5tP7tvg.cjs} +2 -2
- package/dist/{makeManagedServerProvider-B_lPFNpL.mjs → makeManagedServerProvider-B65ipCxM.mjs} +2 -2
- package/dist/resource-monitor/linux-arm64/ryco-resource-monitor +0 -0
- package/dist/resource-monitor/ryco-resource-monitor +0 -0
- package/dist/resource-monitor/ryco-resource-monitor.exe +0 -0
- package/dist/resource-monitor/win32-arm64/ryco-resource-monitor.exe +0 -0
- package/dist/resource-monitor/win32-x64/ryco-resource-monitor.exe +0 -0
- package/dist/{rolldown-runtime-LGfotAqq.cjs → rolldown-runtime-HbSU0i7K.cjs} +7 -0
- package/dist/{runtimeSession-V3RIy8Ao.mjs → runtimeSession-D_BZ-wRH.mjs} +1 -1
- package/dist/{src-BzVJd3Me.cjs → src-FmeCeBJ9.cjs} +3408 -308
- package/dist/{src-7E1M4gTQ.mjs → src-VTj60bUm.mjs} +2154 -140
- package/package.json +1 -1
- package/dist/AzureDevOpsSourceControlProvider-DmJD1-WL.cjs +0 -190
- package/dist/AzureDevOpsSourceControlProvider-d5Ogsspk.mjs +0 -188
- package/dist/GitLabSourceControlProvider-BZdwXdJl.mjs +0 -186
- package/dist/GitLabSourceControlProvider-CrcBR6KQ.cjs +0 -188
- package/dist/client/assets/AccountSettings-FN7pwqYA.js +0 -1
- package/dist/client/assets/AiFocusSettings-xcDAMaqH.js +0 -1
- package/dist/client/assets/AppearanceSettings-BvsvAOsG.js +0 -1
- package/dist/client/assets/BranchToolbar.logic-DFsXV3Qs.js +0 -3
- package/dist/client/assets/BrowserPanel-D-g_xDZs.js +0 -1
- package/dist/client/assets/ChatRightPanel-Bm906qNi.js +0 -201
- package/dist/client/assets/ChatThreadRouteView-CATwakjI.js +0 -1
- package/dist/client/assets/CommandPaletteDialog-Obv1bvxj.js +0 -1
- package/dist/client/assets/CompositeRoot-CfkIdky8.js +0 -1
- package/dist/client/assets/ComputerUseSettings-ljYmTHea.js +0 -1
- package/dist/client/assets/ConnectionsSettings-CIXs7x_E.js +0 -7
- package/dist/client/assets/ContextHandoffEndpointLabel-BzI4Wi1i.js +0 -4
- package/dist/client/assets/DiagnosticsSettings-DGX_2T2M.js +0 -2
- package/dist/client/assets/DialogHandle-CTtSYpsC.js +0 -1
- package/dist/client/assets/DialogRoot-k2_0SNKA.js +0 -1
- package/dist/client/assets/DialogTitle-Du7CAbaO.js +0 -1
- package/dist/client/assets/DialogTrigger-VpFh8Xza.js +0 -1
- package/dist/client/assets/DraftChatThreadRouteView-Cmc5HpWf.js +0 -1
- package/dist/client/assets/GlassSurface-BlWIlQ0s.js +0 -1
- package/dist/client/assets/HostedConnectionControls-ySHqwr4K.js +0 -1
- package/dist/client/assets/HostedHubRoot-Caz8PElK.js +0 -2
- package/dist/client/assets/HostedNodeRenameDialog-ChxJdr-Z.js +0 -1
- package/dist/client/assets/HostedRelayTrustNotice-DPVrIfj_.js +0 -1
- package/dist/client/assets/IntegrationsSettingsPanel-eAq5h-D5.js +0 -2
- package/dist/client/assets/KeybindingsSettings-D1XaEVGH.js +0 -7
- package/dist/client/assets/LazyBrowserPanel-B6POLJh3.js +0 -2
- package/dist/client/assets/McpServersSettings-DqLA6n5v.js +0 -6
- package/dist/client/assets/MobileSheet-CtQIYRlY.js +0 -1
- package/dist/client/assets/NodeSecuritySettings-Skm7GQDl.js +0 -1
- package/dist/client/assets/NullStore-b8PhjwPZ.js +0 -1
- package/dist/client/assets/OnboardingDialog-8-fKvY5H.js +0 -1
- package/dist/client/assets/OpinionatedPluginsSettings-BIJfJ34L.js +0 -1
- package/dist/client/assets/PhoneHome-CUzxhhR4.js +0 -1
- package/dist/client/assets/PhoneSettingsSurface-BK2RHmYJ.js +0 -2
- package/dist/client/assets/ProvidersSettingsPanel-DPxL7AsR.js +0 -1
- package/dist/client/assets/RedactedSensitiveText-DH4mWpB6.js +0 -1
- package/dist/client/assets/RootAppShell-DwiWD4Cy.js +0 -7
- package/dist/client/assets/SettingsDialog-CE8qy0qL.js +0 -2
- package/dist/client/assets/SettingsPanels-BYknk6GO.js +0 -3
- package/dist/client/assets/SettingsPanels.editor-64bM0lhg.js +0 -2
- package/dist/client/assets/SourceControlSettings-BbveiAj3.js +0 -1
- package/dist/client/assets/StatisticsPage-CAO_tCeC.js +0 -1
- package/dist/client/assets/StatisticsPanel-SNlgMv2o.js +0 -1
- package/dist/client/assets/StatisticsSettingsLink-BpkNubL2.js +0 -1
- package/dist/client/assets/ThreadTerminalDrawer-9x_kqQPy.js +0 -18
- package/dist/client/assets/ThreadWorkspacePanel-BXE17C4x.js +0 -2073
- package/dist/client/assets/appChrome-B_VWWDFj.js +0 -1
- package/dist/client/assets/architecture-7GRP2DOG-CQNNG5gU.js +0 -1
- package/dist/client/assets/authorization-BugIGt5e.js +0 -1
- package/dist/client/assets/badge-BswOCwph.js +0 -1
- package/dist/client/assets/capabilities-Bj_X9FZ-.js +0 -1
- package/dist/client/assets/channel-BtX-w3CK.js +0 -1
- package/dist/client/assets/chevron-right-Ddq5gL-L.js +0 -1
- package/dist/client/assets/classDiagram-JCYQIIEL-BgDa3HFl.js +0 -1
- package/dist/client/assets/classDiagram-v2-OCEON4UE-BgDa3HFl.js +0 -1
- package/dist/client/assets/compiler-runtime-C4g8XhC5.js +0 -1
- package/dist/client/assets/desktopWorkspace-Ocqt625W.js +0 -1
- package/dist/client/assets/editorPreferences-DVhg0CXd.js +0 -1
- package/dist/client/assets/eventmodeling-NTZA5JFV-DpzGHjmu.js +0 -1
- package/dist/client/assets/flowDiagram-UKHOOZJN-CaDVMm72.js +0 -1
- package/dist/client/assets/hostedConnectionCoordinator-BztHfGNt.js +0 -1
- package/dist/client/assets/hostedPairing-CqhLPQqG.js +0 -1
- package/dist/client/assets/hubRoutes-DExWSvVE.js +0 -1
- package/dist/client/assets/kbd-DKuvGqFP.js +0 -1
- package/dist/client/assets/layers-CwP65mnC.js +0 -1
- package/dist/client/assets/liquidGlass-CA2o_Cq0.js +0 -1
- package/dist/client/assets/liquidGlass.worker-icvLJKHf.js +0 -1
- package/dist/client/assets/main-BqSANuAs.css +0 -2
- package/dist/client/assets/main-RFllWEko.js +0 -9
- package/dist/client/assets/motion-C9rN_t9p.js +0 -1
- package/dist/client/assets/palette-gaqgtube.js +0 -1
- package/dist/client/assets/perfInstrumentation-BT27w4tn.js +0 -1
- package/dist/client/assets/presentationTier-xlIjcFab.js +0 -1
- package/dist/client/assets/primary-CQh_KHGG.js +0 -5
- package/dist/client/assets/providerDriverMeta-LJ2vJxm-.js +0 -1
- package/dist/client/assets/providerStatus-DBGvvkyF.js +0 -1
- package/dist/client/assets/radio-group-BueX1AVO.js +0 -1
- package/dist/client/assets/railroad-abnf-HS5TGJTU-B890iayj.js +0 -1
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-C079Tzw6.js +0 -1
- package/dist/client/assets/railroad-peg-WCYAUIDC-DxvOX5Rh.js +0 -1
- package/dist/client/assets/runtime-OM-uAtaj.js +0 -18
- package/dist/client/assets/scroll-area-BC-LB_zE.js +0 -1
- package/dist/client/assets/separator-yx1r9ZDw.js +0 -1
- package/dist/client/assets/settingsLayout-BbjDM-p0.js +0 -1
- package/dist/client/assets/sheet-CpEwjOWn.js +0 -1
- package/dist/client/assets/sidebar-DayQTDZp.js +0 -1
- package/dist/client/assets/sidebarProjectFolders-DXqx3OlZ.js +0 -4
- package/dist/client/assets/sidebarStatusText-kdA6QMZS.js +0 -1
- package/dist/client/assets/sidebarUndo-BHFfJj0k.js +0 -1
- package/dist/client/assets/square-Cd2WS1gv.js +0 -1
- package/dist/client/assets/src-DHNjfMxR.js +0 -14
- package/dist/client/assets/stateDiagram-v2-UEYNNEHI-BnzsGHaN.js +0 -1
- package/dist/client/assets/storeSelectors-D60AAN01.js +0 -1
- package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-BnA-oDvU.js +0 -8
- package/dist/client/assets/switch-D9inWKdd.js +0 -48
- package/dist/client/assets/toggle-group-YNqzWsK0.js +0 -1
- package/dist/client/assets/toggle-wMq3LLn1.js +0 -1
- package/dist/client/assets/tooltip-mwOCSCXF.js +0 -1
- package/dist/client/assets/usage-DF8oQrsy.js +0 -1
- package/dist/client/assets/useAppSidebarCollapsed-C0jpPtER.js +0 -1
- package/dist/client/assets/useAppearancePreference-Dlh9P8iu.js +0 -1
- package/dist/client/assets/useBaseUiId-Bz8dcyjd.js +0 -1
- package/dist/client/assets/useLabelableId-C8bc9a0F.js +0 -1
- package/dist/client/assets/useNavigate-01O5dMQe.js +0 -1
- package/dist/client/assets/usePhoneThreadActions-BIdh81kF.js +0 -1
- package/dist/client/assets/usePopupViewport-DYYZwfTW.js +0 -1
- package/dist/client/assets/usePresentationTier-hxB7Yv_Q.js +0 -1
- package/dist/client/assets/useScrollLock-BO185DkJ.js +0 -1
- package/dist/client/assets/useTriggerFocusGuards-CMLVr18J.js +0 -1
- package/dist/client/assets/useValueChanged-DSyRHPje.js +0 -1
- package/dist/client/assets/workspaceMetadataProjection-So9-wSEZ.js +0 -1
- package/dist/collectUint8StreamText-BAUF-vzz.mjs +0 -37
- package/dist/collectUint8StreamText-vagpNUMJ.cjs +0 -42
- package/dist/git-BKwGfw8A.mjs +0 -132
- package/dist/git-Dn5wrgFY.cjs +0 -218
- package/dist/sourceControl-B0D1Op29.cjs +0 -253
- package/dist/sourceControl-BiEBxN01.mjs +0 -224
|
@@ -0,0 +1,2053 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Ds as SourceControlProviderError, Ur as CHANGE_REQUEST_FILE_CONTENTS_MAX_BYTES, ks as truncateSourceControlDetailContent } from "./src-VTj60bUm.mjs";
|
|
4
|
+
import { t as decodeJsonResult } from "./schemaJson-CaG1bOvo.mjs";
|
|
5
|
+
import { $r as withUnsupportedChangeRequestOptionGuards, Jr as AzureDevOpsCli, Qr as sourceControlRefFromInput, Xr as SourceControlProvider, Yr as AzureDevOpsCliError, ai as normalizeAzureDevOpsPullRequestRecord, ei as AzureDevOpsCommitRefSchema, ii as azureDevOpsReviewDecisionFromVotes, ni as AzureDevOpsPullRequestSchema, oi as normalizeAzureDevOpsRefName, ri as azureDevOpsIdentityLogin, ti as AzureDevOpsIdentityRefSchema, tr as azureDevOpsDiscovery } from "./bin.mjs";
|
|
6
|
+
import { Clock, DateTime, Effect, Layer, Option, Result, Schema } from "effect";
|
|
7
|
+
/** `GET .../iterations/{id}/changes?$top=` (the documented maximum). */
|
|
8
|
+
const AZURE_DEVOPS_ITERATION_CHANGES_PAGE = 2e3;
|
|
9
|
+
/** Pull request descriptions are capped by the host (Pull Requests - Update). */
|
|
10
|
+
const AZURE_DEVOPS_DESCRIPTION_MAX_CHARS = 4e3;
|
|
11
|
+
/** Git Repositories security namespace and the bits the viewer capabilities read. */
|
|
12
|
+
const AZURE_DEVOPS_GIT_SECURITY_NAMESPACE = "2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87";
|
|
13
|
+
const AZURE_DEVOPS_GIT_PERMISSION_PULL_REQUEST_CONTRIBUTE = 16384;
|
|
14
|
+
/** Policy type ids from the official policy configuration examples. */
|
|
15
|
+
const AZURE_DEVOPS_POLICY_TYPES = {
|
|
16
|
+
build: "0609b952-1397-4640-95ec-e00a01b2c241",
|
|
17
|
+
minimumApprovalCount: "fa4e907d-c16b-4a4c-9dfa-4906e5d171dd",
|
|
18
|
+
requiredReviewers: "fd2167ab-b0be-447a-8ec8-39368250530e",
|
|
19
|
+
mergeStrategy: "fa4e907d-c16b-4a4c-9dfa-4916e5d171ab"
|
|
20
|
+
};
|
|
21
|
+
const EMPTY_GUID = "00000000-0000-0000-0000-000000000000";
|
|
22
|
+
const CommentPositionSchema = Schema.Struct({
|
|
23
|
+
line: Schema.Number,
|
|
24
|
+
offset: Schema.optional(Schema.NullOr(Schema.Number))
|
|
25
|
+
});
|
|
26
|
+
const NullablePosition = Schema.optional(Schema.NullOr(CommentPositionSchema));
|
|
27
|
+
const AzureDevOpsCommentSchema = Schema.Struct({
|
|
28
|
+
id: Schema.Number,
|
|
29
|
+
parentCommentId: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
30
|
+
author: Schema.optional(Schema.NullOr(AzureDevOpsIdentityRefSchema)),
|
|
31
|
+
content: Schema.optional(Schema.NullOr(Schema.String)),
|
|
32
|
+
publishedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
33
|
+
lastUpdatedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
34
|
+
lastContentUpdatedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
35
|
+
/** `CommentType`: unknown, text, codeChange, system. */
|
|
36
|
+
commentType: Schema.optional(Schema.NullOr(Schema.Union([Schema.String, Schema.Number]))),
|
|
37
|
+
isDeleted: Schema.optional(Schema.NullOr(Schema.Boolean))
|
|
38
|
+
});
|
|
39
|
+
const AzureDevOpsThreadSchema = Schema.Struct({
|
|
40
|
+
id: Schema.Number,
|
|
41
|
+
publishedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
42
|
+
lastUpdatedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
43
|
+
comments: Schema.optional(Schema.NullOr(Schema.Array(AzureDevOpsCommentSchema))),
|
|
44
|
+
/** `CommentThreadStatus`: unknown, active, fixed, wontFix, closed, byDesign, pending. */
|
|
45
|
+
status: Schema.optional(Schema.NullOr(Schema.Union([Schema.String, Schema.Number]))),
|
|
46
|
+
threadContext: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
47
|
+
filePath: Schema.optional(Schema.NullOr(Schema.String)),
|
|
48
|
+
leftFileStart: NullablePosition,
|
|
49
|
+
leftFileEnd: NullablePosition,
|
|
50
|
+
rightFileStart: NullablePosition,
|
|
51
|
+
rightFileEnd: NullablePosition
|
|
52
|
+
}))),
|
|
53
|
+
pullRequestThreadContext: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
54
|
+
changeTrackingId: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
55
|
+
iterationContext: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
56
|
+
firstComparingIteration: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
57
|
+
secondComparingIteration: Schema.optional(Schema.NullOr(Schema.Number))
|
|
58
|
+
}))),
|
|
59
|
+
trackingCriteria: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
60
|
+
firstComparingIteration: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
61
|
+
secondComparingIteration: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
62
|
+
origFilePath: Schema.optional(Schema.NullOr(Schema.String)),
|
|
63
|
+
origLeftFileStart: NullablePosition,
|
|
64
|
+
origLeftFileEnd: NullablePosition,
|
|
65
|
+
origRightFileStart: NullablePosition,
|
|
66
|
+
origRightFileEnd: NullablePosition
|
|
67
|
+
})))
|
|
68
|
+
}))),
|
|
69
|
+
properties: Schema.optional(Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown))),
|
|
70
|
+
identities: Schema.optional(Schema.NullOr(Schema.Record(Schema.String, AzureDevOpsIdentityRefSchema))),
|
|
71
|
+
isDeleted: Schema.optional(Schema.NullOr(Schema.Boolean))
|
|
72
|
+
});
|
|
73
|
+
const AzureDevOpsIterationSchema = Schema.Struct({
|
|
74
|
+
id: Schema.Number,
|
|
75
|
+
createdDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
76
|
+
updatedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
77
|
+
/** `IterationReason`: push, forcePush, create, rebase, unknown, retarget, resolveConflicts. */
|
|
78
|
+
reason: Schema.optional(Schema.NullOr(Schema.String)),
|
|
79
|
+
author: Schema.optional(Schema.NullOr(AzureDevOpsIdentityRefSchema)),
|
|
80
|
+
sourceRefCommit: Schema.optional(Schema.NullOr(AzureDevOpsCommitRefSchema)),
|
|
81
|
+
targetRefCommit: Schema.optional(Schema.NullOr(AzureDevOpsCommitRefSchema)),
|
|
82
|
+
commonRefCommit: Schema.optional(Schema.NullOr(AzureDevOpsCommitRefSchema)),
|
|
83
|
+
newTargetRefName: Schema.optional(Schema.NullOr(Schema.String)),
|
|
84
|
+
oldTargetRefName: Schema.optional(Schema.NullOr(Schema.String)),
|
|
85
|
+
push: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
86
|
+
date: Schema.optional(Schema.NullOr(Schema.String)),
|
|
87
|
+
pushedBy: Schema.optional(Schema.NullOr(AzureDevOpsIdentityRefSchema))
|
|
88
|
+
})))
|
|
89
|
+
});
|
|
90
|
+
const AzureDevOpsIterationChangesSchema = Schema.Struct({
|
|
91
|
+
changeEntries: Schema.Array(Schema.Struct({
|
|
92
|
+
changeTrackingId: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
93
|
+
changeId: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
94
|
+
changeType: Schema.optional(Schema.NullOr(Schema.String)),
|
|
95
|
+
originalPath: Schema.optional(Schema.NullOr(Schema.String)),
|
|
96
|
+
item: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
97
|
+
path: Schema.optional(Schema.NullOr(Schema.String)),
|
|
98
|
+
objectId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
99
|
+
originalObjectId: Schema.optional(Schema.NullOr(Schema.String))
|
|
100
|
+
})))
|
|
101
|
+
})),
|
|
102
|
+
nextSkip: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
103
|
+
nextTop: Schema.optional(Schema.NullOr(Schema.Number))
|
|
104
|
+
});
|
|
105
|
+
const AzureDevOpsPolicyEvaluationSchema = Schema.Struct({
|
|
106
|
+
evaluationId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
107
|
+
/** `PolicyEvaluationStatus`: queued, running, approved, rejected, notApplicable, broken. */
|
|
108
|
+
status: Schema.optional(Schema.NullOr(Schema.String)),
|
|
109
|
+
startedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
110
|
+
completedDate: Schema.optional(Schema.NullOr(Schema.String)),
|
|
111
|
+
configuration: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
112
|
+
id: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
113
|
+
isBlocking: Schema.optional(Schema.NullOr(Schema.Boolean)),
|
|
114
|
+
isEnabled: Schema.optional(Schema.NullOr(Schema.Boolean)),
|
|
115
|
+
isDeleted: Schema.optional(Schema.NullOr(Schema.Boolean)),
|
|
116
|
+
settings: Schema.optional(Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown))),
|
|
117
|
+
type: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
118
|
+
id: Schema.optional(Schema.NullOr(Schema.String)),
|
|
119
|
+
displayName: Schema.optional(Schema.NullOr(Schema.String))
|
|
120
|
+
})))
|
|
121
|
+
})))
|
|
122
|
+
});
|
|
123
|
+
/** `GET _apis/connectionData` (`authenticatedUser` is the caller). */
|
|
124
|
+
const AzureDevOpsConnectionDataSchema = Schema.Struct({ authenticatedUser: Schema.Struct({
|
|
125
|
+
id: Schema.String,
|
|
126
|
+
providerDisplayName: Schema.optional(Schema.NullOr(Schema.String)),
|
|
127
|
+
customDisplayName: Schema.optional(Schema.NullOr(Schema.String)),
|
|
128
|
+
properties: Schema.optional(Schema.NullOr(Schema.Struct({ Account: Schema.optional(Schema.NullOr(Schema.Struct({ $value: Schema.optional(Schema.NullOr(Schema.String)) }))) })))
|
|
129
|
+
}) });
|
|
130
|
+
/** `GET _apis/profile/profiles/me` (fallback identity source). */
|
|
131
|
+
const AzureDevOpsProfileSchema = Schema.Struct({
|
|
132
|
+
id: Schema.String,
|
|
133
|
+
displayName: Schema.optional(Schema.NullOr(Schema.String)),
|
|
134
|
+
emailAddress: Schema.optional(Schema.NullOr(Schema.String))
|
|
135
|
+
});
|
|
136
|
+
const AzureDevOpsPermissionsSchema = Schema.Struct({ value: Schema.Array(Schema.Boolean) });
|
|
137
|
+
const AzureDevOpsTeamMemberSchema = Schema.Struct({
|
|
138
|
+
identity: AzureDevOpsIdentityRefSchema,
|
|
139
|
+
isTeamAdmin: Schema.optional(Schema.NullOr(Schema.Boolean))
|
|
140
|
+
});
|
|
141
|
+
const AzureDevOpsTagSchema = Schema.Struct({
|
|
142
|
+
name: Schema.String,
|
|
143
|
+
active: Schema.optional(Schema.NullOr(Schema.Boolean))
|
|
144
|
+
});
|
|
145
|
+
const AzureDevOpsRefUpdateResultSchema = Schema.Struct({
|
|
146
|
+
name: Schema.optional(Schema.NullOr(Schema.String)),
|
|
147
|
+
success: Schema.optional(Schema.NullOr(Schema.Boolean)),
|
|
148
|
+
updateStatus: Schema.optional(Schema.NullOr(Schema.String)),
|
|
149
|
+
customMessage: Schema.optional(Schema.NullOr(Schema.String))
|
|
150
|
+
});
|
|
151
|
+
/** REST list responses are wrapped: `{ count, value }`; `az devops invoke` adds `continuation_token`. */
|
|
152
|
+
function azureDevOpsCollection(item) {
|
|
153
|
+
return Schema.Struct({
|
|
154
|
+
value: Schema.Array(item),
|
|
155
|
+
count: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
156
|
+
continuation_token: Schema.optional(Schema.NullOr(Schema.String))
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function decodeAzureDevOpsJson(schema) {
|
|
160
|
+
return decodeJsonResult(schema);
|
|
161
|
+
}
|
|
162
|
+
function trimmed(value) {
|
|
163
|
+
const text = value?.trim() ?? "";
|
|
164
|
+
return text.length > 0 ? text : null;
|
|
165
|
+
}
|
|
166
|
+
function parseAzureDevOpsDate(value) {
|
|
167
|
+
const text = trimmed(value);
|
|
168
|
+
if (!text) return null;
|
|
169
|
+
const date = new Date(text);
|
|
170
|
+
return Number.isFinite(date.getTime()) && date.getUTCFullYear() > 1 ? DateTime.fromDateUnsafe(date) : null;
|
|
171
|
+
}
|
|
172
|
+
function epochMillis(value) {
|
|
173
|
+
return DateTime.toEpochMillis(value);
|
|
174
|
+
}
|
|
175
|
+
function isPositiveLine(value) {
|
|
176
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
177
|
+
}
|
|
178
|
+
function isGitObjectId(value) {
|
|
179
|
+
return /^[0-9a-f]{7,64}$/iu.test(value);
|
|
180
|
+
}
|
|
181
|
+
/** Normalizes Azure identity ids ("d6245f20-2af8-…" and "d6245f202af8…" both occur). */
|
|
182
|
+
function identityKey(id) {
|
|
183
|
+
const key = id?.replace(/[-{}]/gu, "").trim().toLowerCase() ?? "";
|
|
184
|
+
return key.length > 0 && key !== EMPTY_GUID.replace(/-/gu, "") ? key : null;
|
|
185
|
+
}
|
|
186
|
+
function sameAzureDevOpsIdentity(left, right) {
|
|
187
|
+
const a = identityKey(left);
|
|
188
|
+
return a !== null && a === identityKey(right);
|
|
189
|
+
}
|
|
190
|
+
function toActor(identity) {
|
|
191
|
+
const login = azureDevOpsIdentityLogin(identity);
|
|
192
|
+
if (!login) return null;
|
|
193
|
+
const avatarUrl = trimmed(identity?.imageUrl);
|
|
194
|
+
return {
|
|
195
|
+
login,
|
|
196
|
+
...avatarUrl ? { avatarUrl } : {}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function threadProperty(thread, key) {
|
|
200
|
+
const entry = thread.properties?.[key];
|
|
201
|
+
if (entry === null || typeof entry !== "object" || !("$value" in entry)) return null;
|
|
202
|
+
const value = entry.$value;
|
|
203
|
+
if (typeof value === "string") return trimmed(value);
|
|
204
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function isSystemComment(comment) {
|
|
208
|
+
const type = comment.commentType;
|
|
209
|
+
return type === "system" || type === 3;
|
|
210
|
+
}
|
|
211
|
+
function liveTextComments(thread) {
|
|
212
|
+
return (thread.comments ?? []).filter((comment) => comment.isDeleted !== true && !isSystemComment(comment));
|
|
213
|
+
}
|
|
214
|
+
/** Comment ids are per thread on Azure, so the page carries `threadId:commentId`. */
|
|
215
|
+
function encodeAzureDevOpsCommentId(threadId, commentId) {
|
|
216
|
+
return `${threadId}:${commentId}`;
|
|
217
|
+
}
|
|
218
|
+
function decodeAzureDevOpsCommentId(value) {
|
|
219
|
+
const match = /^(\d+):(\d+)$/u.exec(value.trim());
|
|
220
|
+
if (!match) return null;
|
|
221
|
+
const threadId = Number(match[1]);
|
|
222
|
+
const commentId = Number(match[2]);
|
|
223
|
+
return threadId > 0 && commentId > 0 ? {
|
|
224
|
+
threadId,
|
|
225
|
+
commentId
|
|
226
|
+
} : null;
|
|
227
|
+
}
|
|
228
|
+
function decodeAzureDevOpsThreadId(value) {
|
|
229
|
+
const match = /^(\d+)$/u.exec(value.trim());
|
|
230
|
+
const id = match ? Number(match[1]) : NaN;
|
|
231
|
+
return Number.isSafeInteger(id) && id > 0 ? id : null;
|
|
232
|
+
}
|
|
233
|
+
/** Every identity the payloads mention, keyed by normalized id, for system-thread references. */
|
|
234
|
+
function collectAzureDevOpsIdentities(input) {
|
|
235
|
+
const identities = /* @__PURE__ */ new Map();
|
|
236
|
+
const add = (identity) => {
|
|
237
|
+
const key = identityKey(identity?.id);
|
|
238
|
+
if (key && identity && !identities.has(key)) identities.set(key, identity);
|
|
239
|
+
};
|
|
240
|
+
add(input.pullRequest.createdBy);
|
|
241
|
+
add(input.pullRequest.closedBy);
|
|
242
|
+
add(input.pullRequest.autoCompleteSetBy);
|
|
243
|
+
for (const reviewer of input.pullRequest.reviewers ?? []) add(reviewer);
|
|
244
|
+
for (const thread of input.threads) {
|
|
245
|
+
for (const comment of thread.comments ?? []) add(comment.author);
|
|
246
|
+
for (const identity of Object.values(thread.identities ?? {})) add(identity);
|
|
247
|
+
}
|
|
248
|
+
return identities;
|
|
249
|
+
}
|
|
250
|
+
function referencedActor(identities, id, displayName) {
|
|
251
|
+
const key = identityKey(id);
|
|
252
|
+
return toActor(key ? identities.get(key) : void 0) ?? (displayName ? { login: displayName } : null);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Viewer capabilities from the caller's identity and repository-level Git
|
|
256
|
+
* permissions. Unknown permissions fall back to what authorship alone proves
|
|
257
|
+
* (the author can edit and comment on their own pull request).
|
|
258
|
+
*/
|
|
259
|
+
function toAzureDevOpsViewerCapabilities(input) {
|
|
260
|
+
const pullRequest = input.pullRequest;
|
|
261
|
+
const isAuthor = sameAzureDevOpsIdentity(pullRequest.createdBy?.id, input.viewer.id);
|
|
262
|
+
const isActive = pullRequest.status.trim().toLowerCase() === "active";
|
|
263
|
+
const contribute = input.permissions?.pullRequestContribute ?? isAuthor;
|
|
264
|
+
const canComplete = input.permissions?.genericContribute ?? false;
|
|
265
|
+
const autoCompleteOn = identityKey(pullRequest.autoCompleteSetBy?.id) !== null;
|
|
266
|
+
return {
|
|
267
|
+
login: input.viewer.login,
|
|
268
|
+
isAuthor,
|
|
269
|
+
canUpdate: isAuthor || contribute,
|
|
270
|
+
canMerge: isActive && canComplete,
|
|
271
|
+
canReview: contribute,
|
|
272
|
+
canUpdateBranch: false,
|
|
273
|
+
canEnableAutoMerge: isActive && canComplete && !autoCompleteOn,
|
|
274
|
+
canDisableAutoMerge: isActive && autoCompleteOn && (canComplete || sameAzureDevOpsIdentity(pullRequest.autoCompleteSetBy?.id, input.viewer.id))
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/** Whether the viewer may comment, reply and change thread status. */
|
|
278
|
+
function azureDevOpsViewerCanComment(viewer) {
|
|
279
|
+
return viewer?.canReview === true;
|
|
280
|
+
}
|
|
281
|
+
function toReviewComment(threadId, comment, viewerId) {
|
|
282
|
+
const createdAt = parseAzureDevOpsDate(comment.publishedDate);
|
|
283
|
+
if (!createdAt) return null;
|
|
284
|
+
const author = toActor(comment.author) ?? { login: "unknown" };
|
|
285
|
+
const edited = parseAzureDevOpsDate(comment.lastContentUpdatedDate);
|
|
286
|
+
const own = viewerId !== null && sameAzureDevOpsIdentity(comment.author?.id, viewerId);
|
|
287
|
+
return {
|
|
288
|
+
id: encodeAzureDevOpsCommentId(threadId, comment.id),
|
|
289
|
+
author,
|
|
290
|
+
body: comment.content ?? "",
|
|
291
|
+
createdAt,
|
|
292
|
+
...edited && epochMillis(edited) > epochMillis(createdAt) ? { updatedAt: edited } : {},
|
|
293
|
+
state: "submitted",
|
|
294
|
+
viewerCanUpdate: own,
|
|
295
|
+
viewerCanDelete: own
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/** Flattened text comments for the composer detail (every live thread, oldest first). */
|
|
299
|
+
function flattenAzureDevOpsThreadComments(threads) {
|
|
300
|
+
return threads.filter((thread) => thread.isDeleted !== true).flatMap((thread) => liveTextComments(thread).filter((comment) => (comment.content?.trim() ?? "").length > 0).map((comment) => ({
|
|
301
|
+
id: encodeAzureDevOpsCommentId(thread.id, comment.id),
|
|
302
|
+
author: azureDevOpsIdentityLogin(comment.author) ?? "unknown",
|
|
303
|
+
body: comment.content ?? "",
|
|
304
|
+
createdAt: comment.publishedDate ?? ""
|
|
305
|
+
}))).filter((comment) => parseAzureDevOpsDate(comment.createdAt) !== null).toSorted((left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt));
|
|
306
|
+
}
|
|
307
|
+
const RESOLVED_THREAD_STATUSES = /* @__PURE__ */ new Set([
|
|
308
|
+
"fixed",
|
|
309
|
+
"closed",
|
|
310
|
+
"wontfix",
|
|
311
|
+
"bydesign"
|
|
312
|
+
]);
|
|
313
|
+
const RESOLVED_THREAD_STATUS_NUMBERS = /* @__PURE__ */ new Set([
|
|
314
|
+
2,
|
|
315
|
+
3,
|
|
316
|
+
4,
|
|
317
|
+
5
|
|
318
|
+
]);
|
|
319
|
+
function isAzureDevOpsThreadResolved(status) {
|
|
320
|
+
if (typeof status === "number") return RESOLVED_THREAD_STATUS_NUMBERS.has(status);
|
|
321
|
+
return RESOLVED_THREAD_STATUSES.has(status?.trim().toLowerCase() ?? "");
|
|
322
|
+
}
|
|
323
|
+
function threadFilePath(thread) {
|
|
324
|
+
const path = trimmed(thread.threadContext?.filePath)?.replace(/^\/+/u, "");
|
|
325
|
+
return path && path.length > 0 ? path : null;
|
|
326
|
+
}
|
|
327
|
+
function anchorFrom(left, right) {
|
|
328
|
+
if (right.start || right.end) return {
|
|
329
|
+
side: "right",
|
|
330
|
+
start: right.start ?? right.end ?? null,
|
|
331
|
+
end: right.end ?? right.start ?? null
|
|
332
|
+
};
|
|
333
|
+
if (left.start || left.end) return {
|
|
334
|
+
side: "left",
|
|
335
|
+
start: left.start ?? left.end ?? null,
|
|
336
|
+
end: left.end ?? left.start ?? null
|
|
337
|
+
};
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* One file thread on the page. Positions are read as the host tracked them to
|
|
342
|
+
* `latestIterationId` (threads are listed with `$iteration=<latest>`): a thread
|
|
343
|
+
* whose position still belongs to an older iteration could not be tracked and
|
|
344
|
+
* is outdated (`line: null`, written position in `originalLine`).
|
|
345
|
+
*/
|
|
346
|
+
function toAzureDevOpsReviewThread(input) {
|
|
347
|
+
const thread = input.thread;
|
|
348
|
+
if (thread.isDeleted === true) return null;
|
|
349
|
+
const path = threadFilePath(thread);
|
|
350
|
+
if (!path) return null;
|
|
351
|
+
const comments = liveTextComments(thread).map((comment) => toReviewComment(thread.id, comment, input.viewerId)).filter((comment) => comment !== null);
|
|
352
|
+
if (comments.length === 0) return null;
|
|
353
|
+
const context = thread.threadContext;
|
|
354
|
+
const current = anchorFrom({
|
|
355
|
+
start: context?.leftFileStart,
|
|
356
|
+
end: context?.leftFileEnd
|
|
357
|
+
}, {
|
|
358
|
+
start: context?.rightFileStart,
|
|
359
|
+
end: context?.rightFileEnd
|
|
360
|
+
});
|
|
361
|
+
const tracking = thread.pullRequestThreadContext?.trackingCriteria ?? null;
|
|
362
|
+
const tracked = tracking !== null && (tracking.secondComparingIteration ?? 0) > 0;
|
|
363
|
+
const original = tracked ? anchorFrom({
|
|
364
|
+
start: tracking.origLeftFileStart,
|
|
365
|
+
end: tracking.origLeftFileEnd
|
|
366
|
+
}, {
|
|
367
|
+
start: tracking.origRightFileStart,
|
|
368
|
+
end: tracking.origRightFileEnd
|
|
369
|
+
}) : current;
|
|
370
|
+
const writtenIteration = thread.pullRequestThreadContext?.iterationContext?.secondComparingIteration ?? null;
|
|
371
|
+
const positionIteration = tracked ? tracking.secondComparingIteration ?? null : writtenIteration;
|
|
372
|
+
const latestIteration = input.iterations.reduce((latest, iteration) => latest === null || iteration.id > latest ? iteration.id : latest, null);
|
|
373
|
+
const isOutdated = current !== null && positionIteration !== null && latestIteration !== null && positionIteration < latestIteration;
|
|
374
|
+
const endLine = current?.end?.line;
|
|
375
|
+
const startLine = current?.start?.line;
|
|
376
|
+
const originalEnd = original?.end?.line;
|
|
377
|
+
const originalStart = original?.start?.line;
|
|
378
|
+
const originalCommitOid = trimmed(input.iterations.find((iteration) => iteration.id === writtenIteration)?.sourceRefCommit?.commitId);
|
|
379
|
+
const resolved = isAzureDevOpsThreadResolved(thread.status);
|
|
380
|
+
const canComment = azureDevOpsViewerCanComment(input.viewer);
|
|
381
|
+
const line = !isOutdated && isPositiveLine(endLine) ? endLine : null;
|
|
382
|
+
return {
|
|
383
|
+
id: String(thread.id),
|
|
384
|
+
path,
|
|
385
|
+
subjectType: current ? "line" : "file",
|
|
386
|
+
side: current?.side ?? "right",
|
|
387
|
+
...current && isPositiveLine(startLine) && startLine !== endLine && !isOutdated ? {
|
|
388
|
+
startLine,
|
|
389
|
+
startSide: current.side
|
|
390
|
+
} : {},
|
|
391
|
+
line,
|
|
392
|
+
...isPositiveLine(originalEnd) ? { originalLine: originalEnd } : {},
|
|
393
|
+
...isPositiveLine(originalStart) && originalStart !== originalEnd ? { originalStartLine: originalStart } : {},
|
|
394
|
+
...originalCommitOid ? { originalCommitOid } : {},
|
|
395
|
+
isResolved: resolved,
|
|
396
|
+
isOutdated,
|
|
397
|
+
viewerCanReply: canComment,
|
|
398
|
+
viewerCanResolve: canComment && !resolved,
|
|
399
|
+
viewerCanUnresolve: canComment && resolved,
|
|
400
|
+
comments,
|
|
401
|
+
totalComments: comments.length
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function toAzureDevOpsReviewThreads(input) {
|
|
405
|
+
const mapped = input.threads.map((thread) => toAzureDevOpsReviewThread({
|
|
406
|
+
thread,
|
|
407
|
+
iterations: input.iterations,
|
|
408
|
+
viewer: input.viewer,
|
|
409
|
+
viewerId: input.viewerId
|
|
410
|
+
})).filter((thread) => thread !== null);
|
|
411
|
+
if (mapped.length <= 200) return {
|
|
412
|
+
threads: mapped,
|
|
413
|
+
truncated: false
|
|
414
|
+
};
|
|
415
|
+
const newest = mapped.toSorted((left, right) => epochMillis(right.comments[0].createdAt) - epochMillis(left.comments[0].createdAt)).slice(0, 200);
|
|
416
|
+
const kept = new Set(newest.map((thread) => thread.id));
|
|
417
|
+
return {
|
|
418
|
+
threads: mapped.filter((thread) => kept.has(thread.id)),
|
|
419
|
+
truncated: true
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/** Vote value → review state (`IdentityRefWithVote.vote`). Reset votes (0) are not reviews. */
|
|
423
|
+
function azureDevOpsVoteReviewState(vote) {
|
|
424
|
+
if (vote > 0) return "approved";
|
|
425
|
+
if (vote < 0) return "changes_requested";
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
function splitCommitMessage(message) {
|
|
429
|
+
const normalized = message.replace(/\r\n/gu, "\n");
|
|
430
|
+
const newline = normalized.indexOf("\n");
|
|
431
|
+
if (newline < 0) return {
|
|
432
|
+
headline: normalized.trim(),
|
|
433
|
+
body: null
|
|
434
|
+
};
|
|
435
|
+
const body = normalized.slice(newline + 1).trim();
|
|
436
|
+
return {
|
|
437
|
+
headline: normalized.slice(0, newline).trim(),
|
|
438
|
+
body: body.length > 0 ? body : null
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function toAzureDevOpsChangeRequestCommit(commit) {
|
|
442
|
+
const { headline } = splitCommitMessage(commit.comment ?? "");
|
|
443
|
+
const author = trimmed(commit.author?.name) ?? trimmed(commit.author?.email);
|
|
444
|
+
const committedDate = trimmed(commit.committer?.date) ?? trimmed(commit.author?.date);
|
|
445
|
+
return {
|
|
446
|
+
oid: commit.commitId,
|
|
447
|
+
shortOid: commit.commitId.slice(0, 7),
|
|
448
|
+
messageHeadline: headline,
|
|
449
|
+
...committedDate ? { committedDate } : {},
|
|
450
|
+
...author ? { author } : {}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Chronological timeline from what Azure records:
|
|
455
|
+
* - text threads without a file (general discussion) → `comment` per comment;
|
|
456
|
+
* - system threads (`properties.CodeReviewThreadType`): `VoteUpdate` → `review`,
|
|
457
|
+
* `ReviewersUpdate` → `review-requested` / `review-request-removed`,
|
|
458
|
+
* `StatusUpdate` → `merged` / `closed` / `reopened`;
|
|
459
|
+
* - pull request commits → `commit`; iterations → `force-pushed` and
|
|
460
|
+
* `base-ref-changed`;
|
|
461
|
+
* - the pull request's own completion / abandonment when no status thread
|
|
462
|
+
* recorded it.
|
|
463
|
+
*/
|
|
464
|
+
function toAzureDevOpsTimeline(input) {
|
|
465
|
+
const identities = collectAzureDevOpsIdentities({
|
|
466
|
+
pullRequest: input.pullRequest,
|
|
467
|
+
threads: input.threads
|
|
468
|
+
});
|
|
469
|
+
const entries = [];
|
|
470
|
+
const push = (item) => entries.push({
|
|
471
|
+
order: entries.length,
|
|
472
|
+
item
|
|
473
|
+
});
|
|
474
|
+
let statusRecorded = false;
|
|
475
|
+
for (const thread of input.threads) {
|
|
476
|
+
if (thread.isDeleted === true) continue;
|
|
477
|
+
const threadType = threadProperty(thread, "CodeReviewThreadType");
|
|
478
|
+
const createdAt = parseAzureDevOpsDate(thread.publishedDate ?? thread.comments?.[0]?.publishedDate);
|
|
479
|
+
if (threadType) {
|
|
480
|
+
if (!createdAt) continue;
|
|
481
|
+
switch (threadType) {
|
|
482
|
+
case "VoteUpdate": {
|
|
483
|
+
const vote = Number(threadProperty(thread, "CodeReviewVoteResult"));
|
|
484
|
+
const state = Number.isFinite(vote) ? azureDevOpsVoteReviewState(vote) : null;
|
|
485
|
+
if (!state) break;
|
|
486
|
+
const actor = referencedActor(identities, threadProperty(thread, "CodeReviewVotedByTfId"), threadProperty(thread, "CodeReviewVotedByDisplayName"));
|
|
487
|
+
push({
|
|
488
|
+
kind: "review",
|
|
489
|
+
id: `vote:${thread.id}`,
|
|
490
|
+
createdAt,
|
|
491
|
+
...actor ? { actor } : {},
|
|
492
|
+
state,
|
|
493
|
+
body: "",
|
|
494
|
+
threadIds: []
|
|
495
|
+
});
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case "ReviewersUpdate": {
|
|
499
|
+
const actor = referencedActor(identities, threadProperty(thread, "CodeReviewReviewersUpdatedByTfId"), threadProperty(thread, "CodeReviewReviewersUpdatedByDisplayname") ?? threadProperty(thread, "CodeReviewReviewersUpdatedByDisplayName"));
|
|
500
|
+
for (const [kind, prefix] of [["review-requested", "CodeReviewReviewersUpdatedAdded"], ["review-request-removed", "CodeReviewReviewersUpdatedRemoved"]]) {
|
|
501
|
+
const tfId = threadProperty(thread, `${prefix}TfId`);
|
|
502
|
+
const reviewer = referencedActor(identities, tfId, threadProperty(thread, `${prefix}DisplayName`));
|
|
503
|
+
if (!reviewer) continue;
|
|
504
|
+
const known = identityKey(tfId);
|
|
505
|
+
const isTeam = known ? identities.get(known)?.isContainer === true : false;
|
|
506
|
+
push({
|
|
507
|
+
kind,
|
|
508
|
+
id: `${kind}:${thread.id}`,
|
|
509
|
+
createdAt,
|
|
510
|
+
...actor ? { actor } : {},
|
|
511
|
+
reviewer: reviewer.login,
|
|
512
|
+
reviewerKind: isTeam ? "team" : "user"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
case "StatusUpdate": {
|
|
518
|
+
const status = threadProperty(thread, "CodeReviewStatus")?.toLowerCase();
|
|
519
|
+
const actor = referencedActor(identities, threadProperty(thread, "CodeReviewStatusUpdatedByTfId"), threadProperty(thread, "CodeReviewStatusUpdatedByDisplayName"));
|
|
520
|
+
const actorField = actor ? { actor } : {};
|
|
521
|
+
if (status === "completed") {
|
|
522
|
+
statusRecorded = statusRecorded || input.pullRequest.status === "completed";
|
|
523
|
+
const commitOid = trimmed(input.pullRequest.lastMergeCommit?.commitId);
|
|
524
|
+
push({
|
|
525
|
+
kind: "merged",
|
|
526
|
+
id: `status:${thread.id}`,
|
|
527
|
+
createdAt,
|
|
528
|
+
...actorField,
|
|
529
|
+
...commitOid ? { commitOid } : {},
|
|
530
|
+
baseRefName: normalizeAzureDevOpsRefName(input.pullRequest.targetRefName)
|
|
531
|
+
});
|
|
532
|
+
} else if (status === "abandoned") {
|
|
533
|
+
statusRecorded = statusRecorded || input.pullRequest.status === "abandoned";
|
|
534
|
+
push({
|
|
535
|
+
kind: "closed",
|
|
536
|
+
id: `status:${thread.id}`,
|
|
537
|
+
createdAt,
|
|
538
|
+
...actorField
|
|
539
|
+
});
|
|
540
|
+
} else if (status === "active" || status === "reactivated") push({
|
|
541
|
+
kind: "reopened",
|
|
542
|
+
id: `status:${thread.id}`,
|
|
543
|
+
createdAt,
|
|
544
|
+
...actorField
|
|
545
|
+
});
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
if (threadFilePath(thread)) continue;
|
|
552
|
+
for (const comment of liveTextComments(thread)) {
|
|
553
|
+
const reviewComment = toReviewComment(thread.id, comment, input.viewerId);
|
|
554
|
+
if (!reviewComment) continue;
|
|
555
|
+
push({
|
|
556
|
+
kind: "comment",
|
|
557
|
+
id: reviewComment.id,
|
|
558
|
+
createdAt: reviewComment.createdAt,
|
|
559
|
+
actor: reviewComment.author,
|
|
560
|
+
body: reviewComment.body,
|
|
561
|
+
...reviewComment.updatedAt ? { updatedAt: reviewComment.updatedAt } : {},
|
|
562
|
+
viewerCanUpdate: reviewComment.viewerCanUpdate === true,
|
|
563
|
+
viewerCanDelete: reviewComment.viewerCanDelete === true
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
for (const commit of input.commits) {
|
|
568
|
+
const createdAt = parseAzureDevOpsDate(commit.committer?.date ?? commit.author?.date);
|
|
569
|
+
if (!createdAt) continue;
|
|
570
|
+
const { headline, body } = splitCommitMessage(commit.comment ?? "");
|
|
571
|
+
const authorName = trimmed(commit.author?.name);
|
|
572
|
+
push({
|
|
573
|
+
kind: "commit",
|
|
574
|
+
id: `commit:${commit.commitId}`,
|
|
575
|
+
createdAt,
|
|
576
|
+
...authorName ? { actor: { login: authorName } } : {},
|
|
577
|
+
oid: commit.commitId,
|
|
578
|
+
shortOid: commit.commitId.slice(0, 7),
|
|
579
|
+
messageHeadline: headline,
|
|
580
|
+
...body ? { messageBody: body } : {}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
const iterations = input.iterations.toSorted((left, right) => left.id - right.id);
|
|
584
|
+
iterations.forEach((iteration, index) => {
|
|
585
|
+
const createdAt = parseAzureDevOpsDate(iteration.createdDate);
|
|
586
|
+
if (!createdAt) return;
|
|
587
|
+
const actor = toActor(iteration.push?.pushedBy ?? iteration.author);
|
|
588
|
+
const reason = iteration.reason?.trim().toLowerCase();
|
|
589
|
+
if (reason === "forcepush" || reason === "rebase") {
|
|
590
|
+
const beforeOid = trimmed(iterations[index - 1]?.sourceRefCommit?.commitId);
|
|
591
|
+
const afterOid = trimmed(iteration.sourceRefCommit?.commitId);
|
|
592
|
+
push({
|
|
593
|
+
kind: "force-pushed",
|
|
594
|
+
id: `iteration:${iteration.id}`,
|
|
595
|
+
createdAt,
|
|
596
|
+
...actor ? { actor } : {},
|
|
597
|
+
...beforeOid ? { beforeOid } : {},
|
|
598
|
+
...afterOid ? { afterOid } : {}
|
|
599
|
+
});
|
|
600
|
+
} else if (reason === "retarget") {
|
|
601
|
+
const previousRefName = trimmed(iteration.oldTargetRefName);
|
|
602
|
+
const currentRefName = trimmed(iteration.newTargetRefName);
|
|
603
|
+
push({
|
|
604
|
+
kind: "base-ref-changed",
|
|
605
|
+
id: `iteration:${iteration.id}`,
|
|
606
|
+
createdAt,
|
|
607
|
+
...actor ? { actor } : {},
|
|
608
|
+
...previousRefName ? { previousRefName: normalizeAzureDevOpsRefName(previousRefName) } : {},
|
|
609
|
+
...currentRefName ? { currentRefName: normalizeAzureDevOpsRefName(currentRefName) } : {}
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
if (!statusRecorded) {
|
|
614
|
+
const status = input.pullRequest.status.trim().toLowerCase();
|
|
615
|
+
const actor = toActor(input.pullRequest.closedBy);
|
|
616
|
+
const closedDate = closedDateOf(input.pullRequest);
|
|
617
|
+
if (closedDate && status === "completed") {
|
|
618
|
+
const commitOid = trimmed(input.pullRequest.lastMergeCommit?.commitId);
|
|
619
|
+
push({
|
|
620
|
+
kind: "merged",
|
|
621
|
+
id: `pull-request:${input.pullRequest.pullRequestId}:merged`,
|
|
622
|
+
createdAt: closedDate,
|
|
623
|
+
...actor ? { actor } : {},
|
|
624
|
+
...commitOid ? { commitOid } : {},
|
|
625
|
+
baseRefName: normalizeAzureDevOpsRefName(input.pullRequest.targetRefName)
|
|
626
|
+
});
|
|
627
|
+
} else if (closedDate && status === "abandoned") push({
|
|
628
|
+
kind: "closed",
|
|
629
|
+
id: `pull-request:${input.pullRequest.pullRequestId}:closed`,
|
|
630
|
+
createdAt: closedDate,
|
|
631
|
+
...actor ? { actor } : {}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
const sorted = entries.toSorted((left, right) => epochMillis(left.item.createdAt) - epochMillis(right.item.createdAt) || left.order - right.order).map((entry) => entry.item);
|
|
635
|
+
const truncated = sorted.length > 300;
|
|
636
|
+
return {
|
|
637
|
+
timeline: truncated ? sorted.slice(sorted.length - 300) : sorted,
|
|
638
|
+
truncated: truncated || input.commitsTruncated
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function closedDateOf(pullRequest) {
|
|
642
|
+
return pullRequest.closedDate ? Option.getOrNull(pullRequest.closedDate) : null;
|
|
643
|
+
}
|
|
644
|
+
function evaluationTypeId(evaluation) {
|
|
645
|
+
return evaluation.configuration?.type?.id?.trim().toLowerCase() ?? "";
|
|
646
|
+
}
|
|
647
|
+
function applicableEvaluations(evaluations) {
|
|
648
|
+
return evaluations.filter((evaluation) => evaluation.configuration?.isEnabled !== false && evaluation.configuration?.isDeleted !== true && evaluation.status?.trim().toLowerCase() !== "notapplicable");
|
|
649
|
+
}
|
|
650
|
+
function statusOf(evaluation) {
|
|
651
|
+
return evaluation.status?.trim().toLowerCase() ?? "";
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Review verdict: votes say when changes were requested; blocking reviewer
|
|
655
|
+
* policies (minimum approvals, required reviewers) say whether approval is
|
|
656
|
+
* still missing. Without policy data only the votes decide.
|
|
657
|
+
*/
|
|
658
|
+
function azureDevOpsReviewDecision(input) {
|
|
659
|
+
const fromVotes = azureDevOpsReviewDecisionFromVotes(input.reviewers);
|
|
660
|
+
if (fromVotes === "changes_requested" || input.evaluations === null) return fromVotes;
|
|
661
|
+
const reviewerPolicies = applicableEvaluations(input.evaluations).filter((evaluation) => {
|
|
662
|
+
const type = evaluationTypeId(evaluation);
|
|
663
|
+
return evaluation.configuration?.isBlocking === true && (type === AZURE_DEVOPS_POLICY_TYPES.minimumApprovalCount || type === AZURE_DEVOPS_POLICY_TYPES.requiredReviewers);
|
|
664
|
+
});
|
|
665
|
+
if (reviewerPolicies.length === 0) return fromVotes;
|
|
666
|
+
if (reviewerPolicies.some((evaluation) => statusOf(evaluation) === "rejected")) return "changes_requested";
|
|
667
|
+
return reviewerPolicies.every((evaluation) => statusOf(evaluation) === "approved") ? "approved" : "review_required";
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Merge state from facts: draft, conflicts, blocking policies that have not
|
|
671
|
+
* passed (`blocked`), optional policies that rejected (`unstable`), a
|
|
672
|
+
* successful test merge with nothing blocking (`clean`); otherwise unknown.
|
|
673
|
+
* Undefined when the policy evaluations could not be read.
|
|
674
|
+
*/
|
|
675
|
+
function azureDevOpsMergeStateStatus(input) {
|
|
676
|
+
const pullRequest = input.pullRequest;
|
|
677
|
+
if (pullRequest.status.trim().toLowerCase() !== "active") return void 0;
|
|
678
|
+
if (pullRequest.isDraft === true) return "draft";
|
|
679
|
+
const mergeStatus = pullRequest.mergeStatus?.trim().toLowerCase();
|
|
680
|
+
if (mergeStatus === "conflicts") return "dirty";
|
|
681
|
+
if (input.evaluations === null) return void 0;
|
|
682
|
+
const applicable = applicableEvaluations(input.evaluations);
|
|
683
|
+
if (applicable.filter((evaluation) => evaluation.configuration?.isBlocking === true).some((evaluation) => statusOf(evaluation) !== "approved")) return "blocked";
|
|
684
|
+
if (mergeStatus === "rejectedbypolicy") return "blocked";
|
|
685
|
+
if (mergeStatus !== "succeeded") return "unknown";
|
|
686
|
+
return applicable.some((evaluation) => statusOf(evaluation) === "rejected") ? "unstable" : "clean";
|
|
687
|
+
}
|
|
688
|
+
function booleanSetting(settings, key) {
|
|
689
|
+
const value = settings[key];
|
|
690
|
+
if (typeof value === "boolean") return value;
|
|
691
|
+
if (value === "true") return true;
|
|
692
|
+
if (value === "false") return false;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Allowed strategies from an enabled "Require a merge strategy" policy
|
|
696
|
+
* (`allowNoFastForward`, `allowSquash`, `allowRebase`, `allowRebaseMerge`, or
|
|
697
|
+
* the legacy `useSquashMerge`); every strategy when no such policy applies.
|
|
698
|
+
*/
|
|
699
|
+
function azureDevOpsMergeCapabilities(evaluations) {
|
|
700
|
+
const policies = applicableEvaluations(evaluations ?? []).filter((evaluation) => evaluationTypeId(evaluation) === AZURE_DEVOPS_POLICY_TYPES.mergeStrategy && evaluation.configuration?.isBlocking !== false);
|
|
701
|
+
let capabilities = {
|
|
702
|
+
merge: true,
|
|
703
|
+
squash: true,
|
|
704
|
+
rebase: true
|
|
705
|
+
};
|
|
706
|
+
for (const policy of policies) {
|
|
707
|
+
const settings = policy.configuration?.settings ?? {};
|
|
708
|
+
const allowNoFastForward = booleanSetting(settings, "allowNoFastForward");
|
|
709
|
+
const allowSquash = booleanSetting(settings, "allowSquash");
|
|
710
|
+
const allowRebase = booleanSetting(settings, "allowRebase");
|
|
711
|
+
const allowed = [
|
|
712
|
+
allowNoFastForward,
|
|
713
|
+
allowSquash,
|
|
714
|
+
allowRebase,
|
|
715
|
+
booleanSetting(settings, "allowRebaseMerge")
|
|
716
|
+
].some((value) => value !== void 0) ? {
|
|
717
|
+
merge: allowNoFastForward === true,
|
|
718
|
+
squash: allowSquash === true,
|
|
719
|
+
rebase: allowRebase === true
|
|
720
|
+
} : booleanSetting(settings, "useSquashMerge") === true ? {
|
|
721
|
+
merge: false,
|
|
722
|
+
squash: true,
|
|
723
|
+
rebase: false
|
|
724
|
+
} : {
|
|
725
|
+
merge: true,
|
|
726
|
+
squash: true,
|
|
727
|
+
rebase: true
|
|
728
|
+
};
|
|
729
|
+
capabilities = {
|
|
730
|
+
merge: capabilities.merge && allowed.merge,
|
|
731
|
+
squash: capabilities.squash && allowed.squash,
|
|
732
|
+
rebase: capabilities.rebase && allowed.rebase
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
return capabilities;
|
|
736
|
+
}
|
|
737
|
+
function azureDevOpsReviewerStates(reviewers) {
|
|
738
|
+
return reviewers.flatMap((reviewer) => {
|
|
739
|
+
const login = azureDevOpsIdentityLogin(reviewer);
|
|
740
|
+
if (!login || reviewer.hasDeclined === true) return [];
|
|
741
|
+
const vote = reviewer.vote ?? 0;
|
|
742
|
+
const avatarUrl = trimmed(reviewer.imageUrl);
|
|
743
|
+
return [{
|
|
744
|
+
login,
|
|
745
|
+
kind: reviewer.isContainer === true ? "team" : "user",
|
|
746
|
+
state: vote > 0 ? "approved" : vote < 0 ? "changes_requested" : "requested",
|
|
747
|
+
...avatarUrl ? { avatarUrl } : {}
|
|
748
|
+
}];
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
const STRATEGY_TO_METHOD = {
|
|
752
|
+
nofastforward: "merge",
|
|
753
|
+
squash: "squash",
|
|
754
|
+
rebase: "rebase",
|
|
755
|
+
rebasemerge: "rebase"
|
|
756
|
+
};
|
|
757
|
+
function azureDevOpsAutoMerge(pullRequest) {
|
|
758
|
+
if (identityKey(pullRequest.autoCompleteSetBy?.id) === null) return null;
|
|
759
|
+
const strategy = pullRequest.completionOptions?.mergeStrategy?.trim().toLowerCase() ?? "";
|
|
760
|
+
const squash = pullRequest.completionOptions?.squashMerge;
|
|
761
|
+
const mergeMethod = STRATEGY_TO_METHOD[strategy] ?? (squash === true || squash === "true" ? "squash" : "merge");
|
|
762
|
+
const enabledBy = azureDevOpsIdentityLogin(pullRequest.autoCompleteSetBy);
|
|
763
|
+
return {
|
|
764
|
+
mergeMethod,
|
|
765
|
+
...enabledBy ? { enabledBy } : {}
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
/** `GitPullRequestMergeStrategy` for a page merge method. */
|
|
769
|
+
function azureDevOpsMergeStrategy(method) {
|
|
770
|
+
switch (method) {
|
|
771
|
+
case "merge": return "noFastForward";
|
|
772
|
+
case "squash": return "squash";
|
|
773
|
+
case "rebase": return "rebase";
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/** Pull Request Threads - Create, general discussion (official example shape). */
|
|
777
|
+
function buildAzureDevOpsCommentThreadBody(content) {
|
|
778
|
+
return {
|
|
779
|
+
comments: [{
|
|
780
|
+
parentCommentId: 0,
|
|
781
|
+
content,
|
|
782
|
+
commentType: 1
|
|
783
|
+
}],
|
|
784
|
+
status: 1
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
/** Pull Request Thread Comments - Create (reply to the thread's first comment). */
|
|
788
|
+
function buildAzureDevOpsReplyBody(input) {
|
|
789
|
+
return {
|
|
790
|
+
content: input.content,
|
|
791
|
+
parentCommentId: input.parentCommentId,
|
|
792
|
+
commentType: 1
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Pull Request Threads - Create on a file (official example shape). `left`
|
|
797
|
+
* anchors on the base side (`leftFile*`), `right` on the head side
|
|
798
|
+
* (`rightFile*`); a range spans `startLine`..`line`. Offsets are 1-based
|
|
799
|
+
* character positions; the end offset covers the whole last line when its
|
|
800
|
+
* length is known.
|
|
801
|
+
*/
|
|
802
|
+
function buildAzureDevOpsFileThreadBody(input) {
|
|
803
|
+
const comment = input.comment;
|
|
804
|
+
const filePath = `/${comment.path.replace(/^\/+/u, "")}`;
|
|
805
|
+
const isFile = comment.subjectType === "file" || comment.line === void 0;
|
|
806
|
+
const side = comment.side ?? "right";
|
|
807
|
+
const endLine = comment.line;
|
|
808
|
+
const startLine = comment.startLine !== void 0 && endLine !== void 0 && comment.startLine < endLine ? comment.startLine : endLine;
|
|
809
|
+
const endOffset = Math.max(1, (input.endLineLength ?? 0) + 1);
|
|
810
|
+
const positions = isFile || endLine === void 0 || startLine === void 0 ? {} : side === "left" ? {
|
|
811
|
+
leftFileStart: {
|
|
812
|
+
line: startLine,
|
|
813
|
+
offset: 1
|
|
814
|
+
},
|
|
815
|
+
leftFileEnd: {
|
|
816
|
+
line: endLine,
|
|
817
|
+
offset: endOffset
|
|
818
|
+
}
|
|
819
|
+
} : {
|
|
820
|
+
rightFileStart: {
|
|
821
|
+
line: startLine,
|
|
822
|
+
offset: 1
|
|
823
|
+
},
|
|
824
|
+
rightFileEnd: {
|
|
825
|
+
line: endLine,
|
|
826
|
+
offset: endOffset
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
return {
|
|
830
|
+
comments: [{
|
|
831
|
+
parentCommentId: 0,
|
|
832
|
+
content: comment.body,
|
|
833
|
+
commentType: 1
|
|
834
|
+
}],
|
|
835
|
+
status: 1,
|
|
836
|
+
threadContext: {
|
|
837
|
+
filePath,
|
|
838
|
+
...positions
|
|
839
|
+
},
|
|
840
|
+
pullRequestThreadContext: {
|
|
841
|
+
...input.changeTrackingId !== null ? { changeTrackingId: input.changeTrackingId } : {},
|
|
842
|
+
iterationContext: {
|
|
843
|
+
firstComparingIteration: 1,
|
|
844
|
+
secondComparingIteration: input.iterationId
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
/** `changeTrackingId` per repository path (no leading slash), including renamed-from paths. */
|
|
850
|
+
function azureDevOpsChangeTrackingIds(changes) {
|
|
851
|
+
const ids = /* @__PURE__ */ new Map();
|
|
852
|
+
for (const entry of changes.changeEntries) {
|
|
853
|
+
const trackingId = entry.changeTrackingId;
|
|
854
|
+
if (typeof trackingId !== "number") continue;
|
|
855
|
+
for (const path of [entry.item?.path, entry.originalPath]) {
|
|
856
|
+
const normalized = trimmed(path)?.replace(/^\/+/u, "");
|
|
857
|
+
if (normalized && !ids.has(normalized)) ids.set(normalized, trackingId);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return ids;
|
|
861
|
+
}
|
|
862
|
+
function latestAzureDevOpsIteration(iterations) {
|
|
863
|
+
return iterations.reduce((latest, iteration) => latest === null || iteration.id > latest.id ? iteration : latest, null);
|
|
864
|
+
}
|
|
865
|
+
function azureDevOpsViewerFromConnectionData(data) {
|
|
866
|
+
const user = data.authenticatedUser;
|
|
867
|
+
if (identityKey(user.id) === null) return null;
|
|
868
|
+
const login = trimmed(user.properties?.Account?.$value) ?? trimmed(user.customDisplayName) ?? trimmed(user.providerDisplayName);
|
|
869
|
+
return login ? {
|
|
870
|
+
id: user.id,
|
|
871
|
+
login
|
|
872
|
+
} : null;
|
|
873
|
+
}
|
|
874
|
+
function azureDevOpsViewerFromProfile(profile) {
|
|
875
|
+
if (identityKey(profile.id) === null) return null;
|
|
876
|
+
const login = trimmed(profile.emailAddress) ?? trimmed(profile.displayName);
|
|
877
|
+
return login ? {
|
|
878
|
+
id: profile.id,
|
|
879
|
+
login
|
|
880
|
+
} : null;
|
|
881
|
+
}
|
|
882
|
+
//#endregion
|
|
883
|
+
//#region src/sourceControl/azureDevOpsPullRequestOperations.ts
|
|
884
|
+
/**
|
|
885
|
+
* Pull request page operations for Azure DevOps, composed from `az repos`
|
|
886
|
+
* commands, `az devops invoke` REST calls (Git REST 7.1) and local `git` for
|
|
887
|
+
* diffs and file contents (Azure has no REST endpoint that returns hunks).
|
|
888
|
+
* Mapping lives in `azureDevOpsPullRequestPage.ts`; this module only fetches,
|
|
889
|
+
* sequences and guards.
|
|
890
|
+
*/
|
|
891
|
+
const AZURE_DEVOPS_DIFF_MAX_BYTES = 8388608;
|
|
892
|
+
const VIEWER_CACHE_TTL_MS = 6e5;
|
|
893
|
+
const ZERO_OBJECT_ID = "0".repeat(40);
|
|
894
|
+
const AZURE_DEVOPS_HEAD_CHANGED_DETAIL = "The pull request's head changed since it was loaded. Refresh and review the new commits, then try again.";
|
|
895
|
+
function cliError(operation, detail, cause) {
|
|
896
|
+
return new AzureDevOpsCliError({
|
|
897
|
+
operation,
|
|
898
|
+
detail,
|
|
899
|
+
...cause !== void 0 ? { cause } : {}
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
function scopeOf(operation, pullRequest) {
|
|
903
|
+
const repositoryId = pullRequest.repository?.id?.trim();
|
|
904
|
+
const projectId = pullRequest.repository?.project?.id?.trim() || null;
|
|
905
|
+
const project = projectId ?? pullRequest.repository?.project?.name?.trim();
|
|
906
|
+
return repositoryId && project ? Effect.succeed({
|
|
907
|
+
project,
|
|
908
|
+
projectId,
|
|
909
|
+
repositoryId,
|
|
910
|
+
pullRequestId: pullRequest.pullRequestId
|
|
911
|
+
}) : Effect.fail(cliError(operation, "Azure DevOps did not report the pull request's project and repository."));
|
|
912
|
+
}
|
|
913
|
+
function route(scope, extra = {}) {
|
|
914
|
+
return {
|
|
915
|
+
project: scope.project,
|
|
916
|
+
repositoryId: scope.repositoryId,
|
|
917
|
+
pullRequestId: scope.pullRequestId,
|
|
918
|
+
...extra
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function decodeWith(operation, schema) {
|
|
922
|
+
const decode = decodeAzureDevOpsJson(schema);
|
|
923
|
+
return (raw) => {
|
|
924
|
+
const decoded = decode(raw);
|
|
925
|
+
return Result.isSuccess(decoded) ? Effect.succeed(decoded.success) : Effect.fail(cliError(operation, "Azure DevOps returned a response Ryco could not read.", decoded.failure));
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function normalizeReference(reference) {
|
|
929
|
+
const trimmed = reference.trim().replace(/^#/u, "");
|
|
930
|
+
return /(?:pullrequest|pull-request|pull|_pulls?)\/(\d+)(?:\D.*)?$/iu.exec(trimmed)?.[1] ?? trimmed;
|
|
931
|
+
}
|
|
932
|
+
function stripHeads(refName) {
|
|
933
|
+
return refName.trim().replace(/^refs\/heads\//u, "");
|
|
934
|
+
}
|
|
935
|
+
function headOf(pullRequest) {
|
|
936
|
+
return pullRequest.lastMergeSourceCommit?.commitId?.trim() || null;
|
|
937
|
+
}
|
|
938
|
+
const ThreadCollection = azureDevOpsCollection(AzureDevOpsThreadSchema);
|
|
939
|
+
const IterationCollection = azureDevOpsCollection(AzureDevOpsIterationSchema);
|
|
940
|
+
const CommitCollection = azureDevOpsCollection(Schema.Struct({
|
|
941
|
+
commitId: Schema.String,
|
|
942
|
+
comment: Schema.optional(Schema.NullOr(Schema.String)),
|
|
943
|
+
author: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
944
|
+
name: Schema.optional(Schema.NullOr(Schema.String)),
|
|
945
|
+
email: Schema.optional(Schema.NullOr(Schema.String)),
|
|
946
|
+
date: Schema.optional(Schema.NullOr(Schema.String))
|
|
947
|
+
}))),
|
|
948
|
+
committer: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
949
|
+
name: Schema.optional(Schema.NullOr(Schema.String)),
|
|
950
|
+
email: Schema.optional(Schema.NullOr(Schema.String)),
|
|
951
|
+
date: Schema.optional(Schema.NullOr(Schema.String))
|
|
952
|
+
})))
|
|
953
|
+
}));
|
|
954
|
+
const PolicyEvaluationList = Schema.Array(AzureDevOpsPolicyEvaluationSchema);
|
|
955
|
+
const TeamMemberList = Schema.Array(AzureDevOpsTeamMemberSchema);
|
|
956
|
+
const TagCollection = azureDevOpsCollection(AzureDevOpsTagSchema);
|
|
957
|
+
const RefUpdateCollection = azureDevOpsCollection(AzureDevOpsRefUpdateResultSchema);
|
|
958
|
+
const ReviewerVoteSchema = Schema.Struct({
|
|
959
|
+
id: Schema.optional(Schema.NullOr(Schema.String)),
|
|
960
|
+
vote: Schema.optional(Schema.NullOr(Schema.Number))
|
|
961
|
+
});
|
|
962
|
+
const RepositorySchema = Schema.Struct({
|
|
963
|
+
id: Schema.optional(Schema.NullOr(Schema.String)),
|
|
964
|
+
project: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
965
|
+
id: Schema.optional(Schema.NullOr(Schema.String)),
|
|
966
|
+
name: Schema.optional(Schema.NullOr(Schema.String))
|
|
967
|
+
})))
|
|
968
|
+
});
|
|
969
|
+
const ProjectSchema = Schema.Struct({
|
|
970
|
+
id: Schema.optional(Schema.NullOr(Schema.String)),
|
|
971
|
+
defaultTeam: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
972
|
+
id: Schema.String,
|
|
973
|
+
name: Schema.optional(Schema.NullOr(Schema.String))
|
|
974
|
+
})))
|
|
975
|
+
});
|
|
976
|
+
function makeAzureDevOpsPullRequestOperations(azure) {
|
|
977
|
+
const viewerCache = /* @__PURE__ */ new Map();
|
|
978
|
+
const executeJson = (cwd, operation, args) => azure.execute({
|
|
979
|
+
cwd,
|
|
980
|
+
args: [
|
|
981
|
+
...args,
|
|
982
|
+
"--only-show-errors",
|
|
983
|
+
"--output",
|
|
984
|
+
"json"
|
|
985
|
+
]
|
|
986
|
+
}).pipe(Effect.map((output) => output.stdout.trim()), Effect.mapError((error) => cliError(operation, error.detail, error)));
|
|
987
|
+
const readPullRequest = (cwd, reference) => azure.getRawPullRequest({
|
|
988
|
+
cwd,
|
|
989
|
+
reference: normalizeReference(reference)
|
|
990
|
+
});
|
|
991
|
+
const listIterations = (cwd, scope) => azure.invoke({
|
|
992
|
+
cwd,
|
|
993
|
+
operation: "listPullRequestIterations",
|
|
994
|
+
area: "git",
|
|
995
|
+
resource: "pullRequestIterations",
|
|
996
|
+
routeParameters: route(scope)
|
|
997
|
+
}).pipe(Effect.flatMap(decodeWith("listPullRequestIterations", IterationCollection)), Effect.map((collection) => collection.value));
|
|
998
|
+
const listThreads = (cwd, scope, iterationId) => azure.invoke({
|
|
999
|
+
cwd,
|
|
1000
|
+
operation: "listPullRequestThreads",
|
|
1001
|
+
area: "git",
|
|
1002
|
+
resource: "pullRequestThreads",
|
|
1003
|
+
routeParameters: route(scope),
|
|
1004
|
+
...iterationId !== null ? { queryParameters: { $iteration: iterationId } } : {}
|
|
1005
|
+
}).pipe(Effect.flatMap(decodeWith("listPullRequestThreads", ThreadCollection)), Effect.map((collection) => collection.value));
|
|
1006
|
+
const getThread = (cwd, scope, threadId, iterationId) => azure.invoke({
|
|
1007
|
+
cwd,
|
|
1008
|
+
operation: "getPullRequestThread",
|
|
1009
|
+
area: "git",
|
|
1010
|
+
resource: "pullRequestThreads",
|
|
1011
|
+
routeParameters: route(scope, { threadId }),
|
|
1012
|
+
...iterationId !== null ? { queryParameters: { $iteration: iterationId } } : {}
|
|
1013
|
+
}).pipe(Effect.flatMap(decodeWith("getPullRequestThread", AzureDevOpsThreadSchema)));
|
|
1014
|
+
const listCommits = (cwd, scope) => azure.invoke({
|
|
1015
|
+
cwd,
|
|
1016
|
+
operation: "listPullRequestCommits",
|
|
1017
|
+
area: "git",
|
|
1018
|
+
resource: "pullRequestCommits",
|
|
1019
|
+
routeParameters: route(scope),
|
|
1020
|
+
queryParameters: { $top: 250 }
|
|
1021
|
+
}).pipe(Effect.flatMap(decodeWith("listPullRequestCommits", CommitCollection)), Effect.map((collection) => ({
|
|
1022
|
+
commits: collection.value.filter((commit) => commit.commitId.trim().length > 0),
|
|
1023
|
+
truncated: Boolean(collection.continuation_token) || collection.value.length >= 250
|
|
1024
|
+
})));
|
|
1025
|
+
const listPolicyEvaluations = (cwd, pullRequestId) => executeJson(cwd, "listPolicyEvaluations", [
|
|
1026
|
+
"repos",
|
|
1027
|
+
"pr",
|
|
1028
|
+
"policy",
|
|
1029
|
+
"list",
|
|
1030
|
+
"--detect",
|
|
1031
|
+
"true",
|
|
1032
|
+
"--id",
|
|
1033
|
+
String(pullRequestId)
|
|
1034
|
+
]).pipe(Effect.flatMap((raw) => raw.length === 0 ? Effect.succeed([]) : decodeWith("listPolicyEvaluations", PolicyEvaluationList)(raw)), Effect.map((evaluations) => evaluations), Effect.catch((error) => Effect.logWarning("Azure DevOps: could not read pull request policies.", {
|
|
1035
|
+
pullRequestId,
|
|
1036
|
+
detail: error.detail
|
|
1037
|
+
}).pipe(Effect.as(null))));
|
|
1038
|
+
/** The signed-in identity: connection data, else the profile; cached per checkout. */
|
|
1039
|
+
const resolveViewer = (cwd) => Effect.gen(function* () {
|
|
1040
|
+
const now = yield* Clock.currentTimeMillis;
|
|
1041
|
+
const cached = viewerCache.get(cwd);
|
|
1042
|
+
if (cached && cached.expiresAt > now) return cached.viewer;
|
|
1043
|
+
const fromConnection = azure.invoke({
|
|
1044
|
+
cwd,
|
|
1045
|
+
operation: "getConnectionData",
|
|
1046
|
+
area: "Location",
|
|
1047
|
+
resource: "connectionData",
|
|
1048
|
+
routeParameters: {},
|
|
1049
|
+
apiVersion: "7.1-preview"
|
|
1050
|
+
}).pipe(Effect.flatMap(decodeWith("getConnectionData", AzureDevOpsConnectionDataSchema)), Effect.map(azureDevOpsViewerFromConnectionData));
|
|
1051
|
+
const fromProfile = azure.invoke({
|
|
1052
|
+
cwd,
|
|
1053
|
+
operation: "getProfile",
|
|
1054
|
+
area: "Profile",
|
|
1055
|
+
resource: "Profiles",
|
|
1056
|
+
routeParameters: { id: "me" }
|
|
1057
|
+
}).pipe(Effect.flatMap(decodeWith("getProfile", AzureDevOpsProfileSchema)), Effect.map(azureDevOpsViewerFromProfile));
|
|
1058
|
+
const viewer = yield* fromConnection.pipe(Effect.flatMap((found) => found ? Effect.succeed(found) : fromProfile), Effect.catch(() => fromProfile), Effect.catch((error) => Effect.logWarning("Azure DevOps: could not resolve the signed-in identity.", { detail: error.detail }).pipe(Effect.as(null))));
|
|
1059
|
+
viewerCache.set(cwd, {
|
|
1060
|
+
viewer,
|
|
1061
|
+
expiresAt: now + VIEWER_CACHE_TTL_MS
|
|
1062
|
+
});
|
|
1063
|
+
return viewer;
|
|
1064
|
+
});
|
|
1065
|
+
/** Repository-level Git permissions of the caller (Security - Has Permissions). */
|
|
1066
|
+
const readPermissions = (cwd, scope) => {
|
|
1067
|
+
if (!scope.projectId) return Effect.succeed(null);
|
|
1068
|
+
const has = (permission) => azure.invoke({
|
|
1069
|
+
cwd,
|
|
1070
|
+
operation: "hasPermissions",
|
|
1071
|
+
area: "Security",
|
|
1072
|
+
resource: "Permissions",
|
|
1073
|
+
routeParameters: {
|
|
1074
|
+
securityNamespaceId: AZURE_DEVOPS_GIT_SECURITY_NAMESPACE,
|
|
1075
|
+
permissions: permission
|
|
1076
|
+
},
|
|
1077
|
+
queryParameters: {
|
|
1078
|
+
tokens: `repoV2/${scope.projectId}/${scope.repositoryId}`,
|
|
1079
|
+
alwaysAllowAdministrators: true
|
|
1080
|
+
}
|
|
1081
|
+
}).pipe(Effect.flatMap(decodeWith("hasPermissions", AzureDevOpsPermissionsSchema)), Effect.map((result) => result.value[0] === true));
|
|
1082
|
+
return Effect.all({
|
|
1083
|
+
pullRequestContribute: has(AZURE_DEVOPS_GIT_PERMISSION_PULL_REQUEST_CONTRIBUTE),
|
|
1084
|
+
genericContribute: has(4)
|
|
1085
|
+
}, { concurrency: 2 }).pipe(Effect.map((permissions) => permissions), Effect.catch((error) => Effect.logWarning("Azure DevOps: could not read repository permissions.", { detail: error.detail }).pipe(Effect.as(null))));
|
|
1086
|
+
};
|
|
1087
|
+
const viewerCapabilities = (cwd, scope, pullRequest) => Effect.all([resolveViewer(cwd), readPermissions(cwd, scope)], { concurrency: 2 }).pipe(Effect.map(([viewer, permissions]) => ({
|
|
1088
|
+
viewerId: viewer?.id ?? null,
|
|
1089
|
+
viewer: viewer ? toAzureDevOpsViewerCapabilities({
|
|
1090
|
+
viewer,
|
|
1091
|
+
permissions,
|
|
1092
|
+
pullRequest
|
|
1093
|
+
}) : null
|
|
1094
|
+
})));
|
|
1095
|
+
const hasCommit = (cwd, sha) => azure.runGit({
|
|
1096
|
+
cwd,
|
|
1097
|
+
operation: "hasCommit",
|
|
1098
|
+
args: [
|
|
1099
|
+
"cat-file",
|
|
1100
|
+
"-e",
|
|
1101
|
+
`${sha}^{commit}`
|
|
1102
|
+
],
|
|
1103
|
+
allowNonZeroExit: true
|
|
1104
|
+
}).pipe(Effect.map((output) => output.exitCode === 0));
|
|
1105
|
+
/**
|
|
1106
|
+
* Make `shas` available in the checkout: fetch them by id, then fall back
|
|
1107
|
+
* to the pull request's refs (`refs/pull/<id>/merge` carries the last test
|
|
1108
|
+
* merge's parents) for servers that refuse unadvertised objects.
|
|
1109
|
+
*/
|
|
1110
|
+
const ensureCommits = (input) => Effect.gen(function* () {
|
|
1111
|
+
for (const sha of input.shas) if (!isGitObjectId(sha)) return yield* cliError(input.operation, `Invalid commit id: ${sha}`);
|
|
1112
|
+
const missing = yield* Effect.filter(input.shas, (sha) => hasCommit(input.cwd, sha).pipe(Effect.map((present) => !present)));
|
|
1113
|
+
if (missing.length === 0) return;
|
|
1114
|
+
yield* azure.runGit({
|
|
1115
|
+
cwd: input.cwd,
|
|
1116
|
+
operation: input.operation,
|
|
1117
|
+
args: [
|
|
1118
|
+
"fetch",
|
|
1119
|
+
"--quiet",
|
|
1120
|
+
"--no-tags",
|
|
1121
|
+
input.remoteName,
|
|
1122
|
+
...missing
|
|
1123
|
+
],
|
|
1124
|
+
allowNonZeroExit: true,
|
|
1125
|
+
timeoutMs: 12e4
|
|
1126
|
+
});
|
|
1127
|
+
const stillMissing = yield* Effect.filter(missing, (sha) => hasCommit(input.cwd, sha).pipe(Effect.map((present) => !present)));
|
|
1128
|
+
if (stillMissing.length === 0) return;
|
|
1129
|
+
const pullRequest = input.pullRequest;
|
|
1130
|
+
if (pullRequest) for (const ref of [
|
|
1131
|
+
`refs/pull/${pullRequest.pullRequestId}/merge`,
|
|
1132
|
+
pullRequest.sourceRefName,
|
|
1133
|
+
pullRequest.targetRefName
|
|
1134
|
+
]) yield* azure.runGit({
|
|
1135
|
+
cwd: input.cwd,
|
|
1136
|
+
operation: input.operation,
|
|
1137
|
+
args: [
|
|
1138
|
+
"fetch",
|
|
1139
|
+
"--quiet",
|
|
1140
|
+
"--no-tags",
|
|
1141
|
+
input.remoteName,
|
|
1142
|
+
ref
|
|
1143
|
+
],
|
|
1144
|
+
allowNonZeroExit: true,
|
|
1145
|
+
timeoutMs: 12e4
|
|
1146
|
+
});
|
|
1147
|
+
const unavailable = yield* Effect.filter(stillMissing, (sha) => hasCommit(input.cwd, sha).pipe(Effect.map((present) => !present)));
|
|
1148
|
+
if (unavailable.length > 0) return yield* cliError(input.operation, `Could not fetch commit ${unavailable[0].slice(0, 7)} from ${input.remoteName}.`);
|
|
1149
|
+
});
|
|
1150
|
+
const gitDiff = (cwd, operation, args) => azure.runGit({
|
|
1151
|
+
cwd,
|
|
1152
|
+
operation,
|
|
1153
|
+
args: [
|
|
1154
|
+
"diff",
|
|
1155
|
+
"--no-color",
|
|
1156
|
+
"--no-ext-diff",
|
|
1157
|
+
"--no-textconv",
|
|
1158
|
+
"--find-renames",
|
|
1159
|
+
"--src-prefix=a/",
|
|
1160
|
+
"--dst-prefix=b/",
|
|
1161
|
+
"--no-relative",
|
|
1162
|
+
"--submodule=short",
|
|
1163
|
+
...args
|
|
1164
|
+
],
|
|
1165
|
+
maxOutputBytes: AZURE_DEVOPS_DIFF_MAX_BYTES,
|
|
1166
|
+
timeoutMs: 12e4
|
|
1167
|
+
}).pipe(Effect.flatMap((output) => output.stdoutTruncated ? Effect.fail(cliError(operation, "This diff is too large to display.")) : Effect.succeed(output.stdout)));
|
|
1168
|
+
const readBlob = (cwd, revision, path) => azure.runGit({
|
|
1169
|
+
cwd,
|
|
1170
|
+
operation: "getChangeRequestFileContents",
|
|
1171
|
+
args: [
|
|
1172
|
+
"cat-file",
|
|
1173
|
+
"blob",
|
|
1174
|
+
`${revision}:${path}`
|
|
1175
|
+
],
|
|
1176
|
+
allowNonZeroExit: true,
|
|
1177
|
+
maxOutputBytes: CHANGE_REQUEST_FILE_CONTENTS_MAX_BYTES
|
|
1178
|
+
}).pipe(Effect.map((output) => {
|
|
1179
|
+
if (output.exitCode !== 0 || output.stdout.includes("\0")) return {
|
|
1180
|
+
contents: null,
|
|
1181
|
+
truncated: false
|
|
1182
|
+
};
|
|
1183
|
+
return {
|
|
1184
|
+
contents: output.stdout,
|
|
1185
|
+
truncated: output.stdoutTruncated
|
|
1186
|
+
};
|
|
1187
|
+
}));
|
|
1188
|
+
const verifyHead = (operation, pullRequest, expected) => {
|
|
1189
|
+
const head = headOf(pullRequest);
|
|
1190
|
+
return head !== null && head.toLowerCase() === expected.trim().toLowerCase() ? Effect.void : Effect.fail(cliError(operation, AZURE_DEVOPS_HEAD_CHANGED_DETAIL));
|
|
1191
|
+
};
|
|
1192
|
+
const findPullRequestCommit = (operation, commits, commitSha, pullRequestId) => {
|
|
1193
|
+
const wanted = commitSha.trim().toLowerCase();
|
|
1194
|
+
const matches = commits.filter((commit) => wanted.length >= 7 ? commit.commitId.toLowerCase().startsWith(wanted) : commit.commitId.toLowerCase() === wanted);
|
|
1195
|
+
return matches.length === 1 ? Effect.succeed(matches[0].commitId) : Effect.fail(cliError(operation, `Commit ${commitSha} is not part of pull request #${pullRequestId}.`));
|
|
1196
|
+
};
|
|
1197
|
+
const getDetail = (input) => Effect.gen(function* () {
|
|
1198
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1199
|
+
const scope = yield* scopeOf("getChangeRequestDetail", pullRequest);
|
|
1200
|
+
const [threads, commits, evaluations] = yield* Effect.all([
|
|
1201
|
+
listThreads(input.cwd, scope, null).pipe(Effect.catch((error) => Effect.logWarning("Azure DevOps: could not read pull request threads.", {
|
|
1202
|
+
pullRequestId: scope.pullRequestId,
|
|
1203
|
+
detail: error.detail
|
|
1204
|
+
}).pipe(Effect.as([])))),
|
|
1205
|
+
listCommits(input.cwd, scope).pipe(Effect.map((page) => page.commits), Effect.catch((error) => Effect.logWarning("Azure DevOps: could not read pull request commits.", {
|
|
1206
|
+
pullRequestId: scope.pullRequestId,
|
|
1207
|
+
detail: error.detail
|
|
1208
|
+
}).pipe(Effect.as([])))),
|
|
1209
|
+
listPolicyEvaluations(input.cwd, scope.pullRequestId)
|
|
1210
|
+
], { concurrency: 3 });
|
|
1211
|
+
return toDetail({
|
|
1212
|
+
pullRequest,
|
|
1213
|
+
threads,
|
|
1214
|
+
commits,
|
|
1215
|
+
evaluations
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1218
|
+
const getActivity = (input) => Effect.gen(function* () {
|
|
1219
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1220
|
+
const scope = yield* scopeOf("getChangeRequestActivity", pullRequest);
|
|
1221
|
+
const [iterations, commitPage, access] = yield* Effect.all([
|
|
1222
|
+
listIterations(input.cwd, scope),
|
|
1223
|
+
listCommits(input.cwd, scope),
|
|
1224
|
+
viewerCapabilities(input.cwd, scope, pullRequest)
|
|
1225
|
+
], { concurrency: 3 });
|
|
1226
|
+
const latest = latestAzureDevOpsIteration(iterations);
|
|
1227
|
+
const threads = yield* listThreads(input.cwd, scope, latest?.id ?? null);
|
|
1228
|
+
const timeline = toAzureDevOpsTimeline({
|
|
1229
|
+
pullRequest,
|
|
1230
|
+
threads,
|
|
1231
|
+
iterations,
|
|
1232
|
+
commits: commitPage.commits,
|
|
1233
|
+
commitsTruncated: commitPage.truncated,
|
|
1234
|
+
viewerId: access.viewerId
|
|
1235
|
+
});
|
|
1236
|
+
const reviewThreads = toAzureDevOpsReviewThreads({
|
|
1237
|
+
threads,
|
|
1238
|
+
iterations,
|
|
1239
|
+
viewer: access.viewer,
|
|
1240
|
+
viewerId: access.viewerId
|
|
1241
|
+
});
|
|
1242
|
+
const headSha = headOf(pullRequest) ?? latest?.sourceRefCommit?.commitId?.trim() ?? null;
|
|
1243
|
+
return {
|
|
1244
|
+
provider: "azure-devops",
|
|
1245
|
+
number: pullRequest.pullRequestId,
|
|
1246
|
+
headSha: headSha && headSha.length > 0 ? headSha : null,
|
|
1247
|
+
viewer: access.viewer,
|
|
1248
|
+
timeline: timeline.timeline,
|
|
1249
|
+
timelineTruncated: timeline.truncated,
|
|
1250
|
+
reviewThreads: reviewThreads.threads,
|
|
1251
|
+
reviewThreadsTruncated: reviewThreads.truncated,
|
|
1252
|
+
pendingReview: null
|
|
1253
|
+
};
|
|
1254
|
+
});
|
|
1255
|
+
const getDiff = (input) => Effect.gen(function* () {
|
|
1256
|
+
const operation = "getChangeRequestDiff";
|
|
1257
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1258
|
+
if (input.expectedHeadSha) yield* verifyHead(operation, pullRequest, input.expectedHeadSha).pipe(Effect.mapError(() => cliError(operation, "Pull request changed while loading the diff. Refresh and try again.")));
|
|
1259
|
+
const commitSha = input.commitSha?.trim();
|
|
1260
|
+
if (commitSha) {
|
|
1261
|
+
const scope = yield* scopeOf(operation, pullRequest);
|
|
1262
|
+
const page = yield* listCommits(input.cwd, scope);
|
|
1263
|
+
const sha = yield* findPullRequestCommit(operation, page.commits, commitSha, pullRequest.pullRequestId);
|
|
1264
|
+
yield* ensureCommits({
|
|
1265
|
+
cwd: input.cwd,
|
|
1266
|
+
operation,
|
|
1267
|
+
remoteName: input.remoteName,
|
|
1268
|
+
shas: [sha],
|
|
1269
|
+
pullRequest
|
|
1270
|
+
});
|
|
1271
|
+
return yield* gitDiff(input.cwd, operation, [`${sha}^1`, sha]);
|
|
1272
|
+
}
|
|
1273
|
+
const head = headOf(pullRequest);
|
|
1274
|
+
const target = pullRequest.lastMergeTargetCommit?.commitId?.trim();
|
|
1275
|
+
if (!head || !target) return yield* cliError(operation, "Azure DevOps has not prepared this pull request's merge yet. Try again shortly.");
|
|
1276
|
+
yield* ensureCommits({
|
|
1277
|
+
cwd: input.cwd,
|
|
1278
|
+
operation,
|
|
1279
|
+
remoteName: input.remoteName,
|
|
1280
|
+
shas: [target, head],
|
|
1281
|
+
pullRequest
|
|
1282
|
+
});
|
|
1283
|
+
return yield* gitDiff(input.cwd, operation, [`${target}...${head}`]);
|
|
1284
|
+
});
|
|
1285
|
+
const getFileContents = (input) => Effect.gen(function* () {
|
|
1286
|
+
const operation = "getChangeRequestFileContents";
|
|
1287
|
+
for (const path of [input.path, input.previousPath]) if (path !== void 0 && !isRepositoryPath(path)) return yield* cliError(operation, `Invalid repository file path: ${path}`);
|
|
1288
|
+
for (const sha of [input.headSha, input.baseSha]) if (sha !== void 0 && !isGitObjectId(sha)) return yield* cliError(operation, `Invalid commit id: ${sha}`);
|
|
1289
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1290
|
+
const target = pullRequest.lastMergeTargetCommit?.commitId?.trim();
|
|
1291
|
+
const baseRevision = input.baseSha ?? target;
|
|
1292
|
+
if (!baseRevision) return yield* cliError(operation, "Azure DevOps has not prepared this pull request's merge yet. Try again shortly.");
|
|
1293
|
+
yield* ensureCommits({
|
|
1294
|
+
cwd: input.cwd,
|
|
1295
|
+
operation,
|
|
1296
|
+
remoteName: input.remoteName,
|
|
1297
|
+
shas: [input.headSha, baseRevision],
|
|
1298
|
+
pullRequest
|
|
1299
|
+
});
|
|
1300
|
+
const baseSha = input.baseSha ? input.baseSha : yield* azure.runGit({
|
|
1301
|
+
cwd: input.cwd,
|
|
1302
|
+
operation,
|
|
1303
|
+
args: [
|
|
1304
|
+
"merge-base",
|
|
1305
|
+
baseRevision,
|
|
1306
|
+
input.headSha
|
|
1307
|
+
]
|
|
1308
|
+
}).pipe(Effect.flatMap((output) => {
|
|
1309
|
+
const sha = output.stdout.trim();
|
|
1310
|
+
return isGitObjectId(sha) ? Effect.succeed(sha) : Effect.fail(cliError(operation, "No merge base for this pull request."));
|
|
1311
|
+
}));
|
|
1312
|
+
const [oldSide, newSide] = yield* Effect.all([readBlob(input.cwd, baseSha, input.previousPath ?? input.path), readBlob(input.cwd, input.headSha, input.path)], { concurrency: 2 });
|
|
1313
|
+
return {
|
|
1314
|
+
path: input.path,
|
|
1315
|
+
oldContents: oldSide.contents,
|
|
1316
|
+
newContents: newSide.contents,
|
|
1317
|
+
truncated: oldSide.truncated || newSide.truncated
|
|
1318
|
+
};
|
|
1319
|
+
});
|
|
1320
|
+
const listChangeRequests = (input) => Effect.gen(function* () {
|
|
1321
|
+
const limit = input.limit ?? 20;
|
|
1322
|
+
const query = input.query?.trim().toLowerCase() ?? "";
|
|
1323
|
+
const top = query.length > 0 ? Math.max(limit * 5, 100) : limit;
|
|
1324
|
+
const base = {
|
|
1325
|
+
cwd: input.cwd,
|
|
1326
|
+
headSelector: input.headSelector,
|
|
1327
|
+
...input.source ? { source: input.source } : {},
|
|
1328
|
+
state: input.state,
|
|
1329
|
+
limit: top
|
|
1330
|
+
};
|
|
1331
|
+
let rows;
|
|
1332
|
+
switch (input.involvement) {
|
|
1333
|
+
case void 0:
|
|
1334
|
+
rows = yield* azure.listPullRequests(base);
|
|
1335
|
+
break;
|
|
1336
|
+
case "authored":
|
|
1337
|
+
rows = yield* azure.listPullRequests({
|
|
1338
|
+
...base,
|
|
1339
|
+
creator: "me"
|
|
1340
|
+
});
|
|
1341
|
+
break;
|
|
1342
|
+
case "review-requested":
|
|
1343
|
+
rows = yield* azure.listPullRequests({
|
|
1344
|
+
...base,
|
|
1345
|
+
reviewer: "me"
|
|
1346
|
+
});
|
|
1347
|
+
break;
|
|
1348
|
+
case "involved": {
|
|
1349
|
+
const [authored, reviewing] = yield* Effect.all([azure.listPullRequests({
|
|
1350
|
+
...base,
|
|
1351
|
+
creator: "me"
|
|
1352
|
+
}), azure.listPullRequests({
|
|
1353
|
+
...base,
|
|
1354
|
+
reviewer: "me"
|
|
1355
|
+
})], { concurrency: 2 });
|
|
1356
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1357
|
+
rows = [...authored, ...reviewing].filter((row) => seen.has(row.number) ? false : (seen.add(row.number), true)).toSorted((left, right) => right.number - left.number);
|
|
1358
|
+
break;
|
|
1359
|
+
}
|
|
1360
|
+
case "assigned":
|
|
1361
|
+
case "mentioned": return yield* cliError("listChangeRequests", `Azure DevOps has no ${input.involvement === "assigned" ? "assignees" : "mentions"} on pull requests to filter by.`);
|
|
1362
|
+
}
|
|
1363
|
+
return (query.length === 0 ? rows : rows.filter((row) => row.title.toLowerCase().includes(query) || `#${row.number}` === query || String(row.number) === query)).slice(0, limit);
|
|
1364
|
+
});
|
|
1365
|
+
const addComment = (input) => Effect.gen(function* () {
|
|
1366
|
+
const scope = yield* scopeOf("addChangeRequestComment", yield* readPullRequest(input.cwd, input.reference));
|
|
1367
|
+
yield* azure.invoke({
|
|
1368
|
+
cwd: input.cwd,
|
|
1369
|
+
operation: "addChangeRequestComment",
|
|
1370
|
+
area: "git",
|
|
1371
|
+
resource: "pullRequestThreads",
|
|
1372
|
+
routeParameters: route(scope),
|
|
1373
|
+
httpMethod: "POST",
|
|
1374
|
+
body: buildAzureDevOpsCommentThreadBody(input.body)
|
|
1375
|
+
});
|
|
1376
|
+
});
|
|
1377
|
+
const readReviewThread = (cwd, scope, pullRequest, threadId) => Effect.gen(function* () {
|
|
1378
|
+
const [iterations, access] = yield* Effect.all([listIterations(cwd, scope), viewerCapabilities(cwd, scope, pullRequest)], { concurrency: 2 });
|
|
1379
|
+
const latest = latestAzureDevOpsIteration(iterations);
|
|
1380
|
+
return toAzureDevOpsReviewThread({
|
|
1381
|
+
thread: yield* getThread(cwd, scope, threadId, latest?.id ?? null),
|
|
1382
|
+
iterations,
|
|
1383
|
+
viewer: access.viewer,
|
|
1384
|
+
viewerId: access.viewerId
|
|
1385
|
+
});
|
|
1386
|
+
});
|
|
1387
|
+
const replyToThread = (input) => Effect.gen(function* () {
|
|
1388
|
+
const operation = "replyToReviewThread";
|
|
1389
|
+
const threadId = decodeAzureDevOpsThreadId(input.threadId);
|
|
1390
|
+
if (threadId === null) return yield* cliError(operation, `Invalid review thread id: ${input.threadId}`);
|
|
1391
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1392
|
+
const scope = yield* scopeOf(operation, pullRequest);
|
|
1393
|
+
const parent = ((yield* getThread(input.cwd, scope, threadId, null)).comments ?? []).find((comment) => comment.isDeleted !== true && (comment.parentCommentId ?? 0) === 0)?.id ?? 1;
|
|
1394
|
+
yield* azure.invoke({
|
|
1395
|
+
cwd: input.cwd,
|
|
1396
|
+
operation,
|
|
1397
|
+
area: "git",
|
|
1398
|
+
resource: "pullRequestThreadComments",
|
|
1399
|
+
routeParameters: route(scope, { threadId }),
|
|
1400
|
+
httpMethod: "POST",
|
|
1401
|
+
body: buildAzureDevOpsReplyBody({
|
|
1402
|
+
content: input.body,
|
|
1403
|
+
parentCommentId: parent
|
|
1404
|
+
})
|
|
1405
|
+
}).pipe(Effect.flatMap(decodeWith(operation, AzureDevOpsCommentSchema)));
|
|
1406
|
+
const updated = yield* readReviewThread(input.cwd, scope, pullRequest, threadId);
|
|
1407
|
+
if (!updated) return yield* cliError(operation, `Thread ${threadId} is not a review thread on a file.`);
|
|
1408
|
+
return updated;
|
|
1409
|
+
});
|
|
1410
|
+
const setThreadResolved = (input) => Effect.gen(function* () {
|
|
1411
|
+
const operation = "setReviewThreadResolved";
|
|
1412
|
+
const threadId = decodeAzureDevOpsThreadId(input.threadId);
|
|
1413
|
+
if (threadId === null) return yield* cliError(operation, `Invalid review thread id: ${input.threadId}`);
|
|
1414
|
+
const scope = yield* scopeOf(operation, yield* readPullRequest(input.cwd, input.reference));
|
|
1415
|
+
const thread = yield* azure.invoke({
|
|
1416
|
+
cwd: input.cwd,
|
|
1417
|
+
operation,
|
|
1418
|
+
area: "git",
|
|
1419
|
+
resource: "pullRequestThreads",
|
|
1420
|
+
routeParameters: route(scope, { threadId }),
|
|
1421
|
+
httpMethod: "PATCH",
|
|
1422
|
+
body: { status: input.resolved ? "fixed" : "active" }
|
|
1423
|
+
}).pipe(Effect.flatMap(decodeWith(operation, AzureDevOpsThreadSchema)));
|
|
1424
|
+
return {
|
|
1425
|
+
threadId: String(thread.id),
|
|
1426
|
+
isResolved: isAzureDevOpsThreadResolved(thread.status)
|
|
1427
|
+
};
|
|
1428
|
+
});
|
|
1429
|
+
const updateComment = (input) => Effect.gen(function* () {
|
|
1430
|
+
const operation = "updateChangeRequestComment";
|
|
1431
|
+
if (input.commentKind === "review") return yield* cliError(operation, "Azure DevOps votes have no body to edit.");
|
|
1432
|
+
const ids = decodeAzureDevOpsCommentId(input.commentId);
|
|
1433
|
+
if (!ids) return yield* cliError(operation, `Invalid comment id: ${input.commentId}`);
|
|
1434
|
+
const routeParameters = route(yield* scopeOf(operation, yield* readPullRequest(input.cwd, input.reference)), {
|
|
1435
|
+
threadId: ids.threadId,
|
|
1436
|
+
commentId: ids.commentId
|
|
1437
|
+
});
|
|
1438
|
+
if (input.action === "delete") {
|
|
1439
|
+
yield* azure.invoke({
|
|
1440
|
+
cwd: input.cwd,
|
|
1441
|
+
operation,
|
|
1442
|
+
area: "git",
|
|
1443
|
+
resource: "pullRequestThreadComments",
|
|
1444
|
+
routeParameters,
|
|
1445
|
+
httpMethod: "DELETE"
|
|
1446
|
+
});
|
|
1447
|
+
return {
|
|
1448
|
+
commentId: input.commentId,
|
|
1449
|
+
deleted: true
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
yield* azure.invoke({
|
|
1453
|
+
cwd: input.cwd,
|
|
1454
|
+
operation,
|
|
1455
|
+
area: "git",
|
|
1456
|
+
resource: "pullRequestThreadComments",
|
|
1457
|
+
routeParameters,
|
|
1458
|
+
httpMethod: "PATCH",
|
|
1459
|
+
body: { content: input.body }
|
|
1460
|
+
}).pipe(Effect.flatMap(decodeWith(operation, AzureDevOpsCommentSchema)));
|
|
1461
|
+
return {
|
|
1462
|
+
commentId: input.commentId,
|
|
1463
|
+
deleted: false
|
|
1464
|
+
};
|
|
1465
|
+
});
|
|
1466
|
+
const submitReview = (input) => Effect.gen(function* () {
|
|
1467
|
+
const operation = "submitChangeRequestReview";
|
|
1468
|
+
const body = input.body?.trim() ?? "";
|
|
1469
|
+
if (input.event === "comment" && body.length === 0 && input.comments.length === 0) return yield* cliError(operation, "Write a comment or add line comments before submitting.");
|
|
1470
|
+
const scope = yield* scopeOf(operation, yield* readPullRequest(input.cwd, input.reference));
|
|
1471
|
+
const latest = latestAzureDevOpsIteration(yield* listIterations(input.cwd, scope));
|
|
1472
|
+
const iterationHead = latest?.sourceRefCommit?.commitId?.trim().toLowerCase();
|
|
1473
|
+
if (!latest || iterationHead !== input.expectedHeadSha.trim().toLowerCase()) return yield* cliError(operation, AZURE_DEVOPS_HEAD_CHANGED_DETAIL);
|
|
1474
|
+
const trackingIds = input.comments.length > 0 ? yield* azure.invoke({
|
|
1475
|
+
cwd: input.cwd,
|
|
1476
|
+
operation,
|
|
1477
|
+
area: "git",
|
|
1478
|
+
resource: "pullRequestIterationChanges",
|
|
1479
|
+
routeParameters: route(scope, { iterationId: latest.id }),
|
|
1480
|
+
queryParameters: {
|
|
1481
|
+
$top: AZURE_DEVOPS_ITERATION_CHANGES_PAGE,
|
|
1482
|
+
$compareTo: 0
|
|
1483
|
+
}
|
|
1484
|
+
}).pipe(Effect.flatMap(decodeWith(operation, AzureDevOpsIterationChangesSchema)), Effect.map(azureDevOpsChangeTrackingIds)) : /* @__PURE__ */ new Map();
|
|
1485
|
+
const createdThreadIds = [];
|
|
1486
|
+
const postThread = (threadBody) => azure.invoke({
|
|
1487
|
+
cwd: input.cwd,
|
|
1488
|
+
operation,
|
|
1489
|
+
area: "git",
|
|
1490
|
+
resource: "pullRequestThreads",
|
|
1491
|
+
routeParameters: route(scope),
|
|
1492
|
+
httpMethod: "POST",
|
|
1493
|
+
body: threadBody
|
|
1494
|
+
}).pipe(Effect.flatMap(decodeWith(operation, AzureDevOpsThreadSchema)), Effect.tap((thread) => Effect.sync(() => createdThreadIds.push(thread.id))));
|
|
1495
|
+
if (body.length > 0) yield* postThread(buildAzureDevOpsCommentThreadBody(body));
|
|
1496
|
+
for (const comment of input.comments) yield* postThread(buildAzureDevOpsFileThreadBody({
|
|
1497
|
+
comment,
|
|
1498
|
+
iterationId: latest.id,
|
|
1499
|
+
changeTrackingId: trackingIds.get(comment.path.replace(/^\/+/u, "")) ?? null
|
|
1500
|
+
}));
|
|
1501
|
+
const vote = input.event === "approve" ? "approve" : input.event === "request_changes" ? "wait-for-author" : null;
|
|
1502
|
+
const url = input.webUrl ?? "";
|
|
1503
|
+
if (vote === null) return {
|
|
1504
|
+
reviewId: `thread:${createdThreadIds[0] ?? 0}`,
|
|
1505
|
+
state: "commented",
|
|
1506
|
+
...url ? { url } : {}
|
|
1507
|
+
};
|
|
1508
|
+
const reviewer = yield* executeJson(input.cwd, operation, [
|
|
1509
|
+
"repos",
|
|
1510
|
+
"pr",
|
|
1511
|
+
"set-vote",
|
|
1512
|
+
"--detect",
|
|
1513
|
+
"true",
|
|
1514
|
+
"--id",
|
|
1515
|
+
String(scope.pullRequestId),
|
|
1516
|
+
"--vote",
|
|
1517
|
+
vote
|
|
1518
|
+
]).pipe(Effect.flatMap(decodeWith(operation, ReviewerVoteSchema)));
|
|
1519
|
+
return {
|
|
1520
|
+
reviewId: `vote:${reviewer.id ?? "me"}:${reviewer.vote ?? (vote === "approve" ? 10 : -5)}`,
|
|
1521
|
+
state: input.event === "approve" ? "approved" : "changes_requested",
|
|
1522
|
+
...url ? { url } : {}
|
|
1523
|
+
};
|
|
1524
|
+
});
|
|
1525
|
+
const patchPullRequest = (cwd, operation, scope, body) => azure.invoke({
|
|
1526
|
+
cwd,
|
|
1527
|
+
operation,
|
|
1528
|
+
area: "git",
|
|
1529
|
+
resource: "pullRequests",
|
|
1530
|
+
routeParameters: route(scope),
|
|
1531
|
+
httpMethod: "PATCH",
|
|
1532
|
+
body
|
|
1533
|
+
}).pipe(Effect.flatMap(decodeWith(operation, AzureDevOpsPullRequestSchema)));
|
|
1534
|
+
const updatePullRequestCli = (cwd, pullRequestId, args) => executeJson(cwd, "updateChangeRequest", [
|
|
1535
|
+
"repos",
|
|
1536
|
+
"pr",
|
|
1537
|
+
"update",
|
|
1538
|
+
"--detect",
|
|
1539
|
+
"true",
|
|
1540
|
+
"--id",
|
|
1541
|
+
String(pullRequestId),
|
|
1542
|
+
...args
|
|
1543
|
+
]);
|
|
1544
|
+
const deleteSourceBranch = (cwd, scope, pullRequest) => Effect.gen(function* () {
|
|
1545
|
+
const operation = "deleteSourceBranch";
|
|
1546
|
+
const head = headOf(pullRequest);
|
|
1547
|
+
if (!head) return yield* cliError(operation, "The head branch's commit is unknown.");
|
|
1548
|
+
const result = (yield* azure.invoke({
|
|
1549
|
+
cwd,
|
|
1550
|
+
operation,
|
|
1551
|
+
area: "git",
|
|
1552
|
+
resource: "refs",
|
|
1553
|
+
routeParameters: {
|
|
1554
|
+
project: scope.project,
|
|
1555
|
+
repositoryId: scope.repositoryId
|
|
1556
|
+
},
|
|
1557
|
+
httpMethod: "POST",
|
|
1558
|
+
body: [{
|
|
1559
|
+
name: pullRequest.sourceRefName,
|
|
1560
|
+
oldObjectId: head,
|
|
1561
|
+
newObjectId: ZERO_OBJECT_ID
|
|
1562
|
+
}]
|
|
1563
|
+
}).pipe(Effect.flatMap(decodeWith(operation, RefUpdateCollection)))).value[0];
|
|
1564
|
+
if (result?.success !== true) return yield* cliError(operation, result?.customMessage?.trim() || `Could not delete ${stripHeads(pullRequest.sourceRefName)} (${result?.updateStatus ?? "unknown status"}).`);
|
|
1565
|
+
});
|
|
1566
|
+
const updatePullRequest = (input) => Effect.gen(function* () {
|
|
1567
|
+
const operation = "updateChangeRequest";
|
|
1568
|
+
const action = input.action;
|
|
1569
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1570
|
+
const scope = yield* scopeOf(operation, pullRequest);
|
|
1571
|
+
const id = scope.pullRequestId;
|
|
1572
|
+
switch (action.kind) {
|
|
1573
|
+
case "edit":
|
|
1574
|
+
if (action.body !== void 0 && action.body.length > 4e3) return yield* cliError(operation, `Azure DevOps descriptions are limited to ${AZURE_DEVOPS_DESCRIPTION_MAX_CHARS} characters.`);
|
|
1575
|
+
if (action.title !== void 0 || action.body !== void 0) {
|
|
1576
|
+
const titleArgs = action.title !== void 0 ? ["--title", action.title] : [];
|
|
1577
|
+
if (action.body !== void 0) yield* azure.executeWithFile({
|
|
1578
|
+
cwd: input.cwd,
|
|
1579
|
+
operation,
|
|
1580
|
+
contents: action.body,
|
|
1581
|
+
suffix: ".md",
|
|
1582
|
+
args: (file) => [
|
|
1583
|
+
"repos",
|
|
1584
|
+
"pr",
|
|
1585
|
+
"update",
|
|
1586
|
+
"--detect",
|
|
1587
|
+
"true",
|
|
1588
|
+
"--id",
|
|
1589
|
+
String(id),
|
|
1590
|
+
...titleArgs,
|
|
1591
|
+
"--description",
|
|
1592
|
+
`@${file}`
|
|
1593
|
+
]
|
|
1594
|
+
});
|
|
1595
|
+
else yield* updatePullRequestCli(input.cwd, id, titleArgs);
|
|
1596
|
+
}
|
|
1597
|
+
if (action.baseRefName !== void 0) yield* patchPullRequest(input.cwd, operation, scope, { targetRefName: `refs/heads/${stripHeads(action.baseRefName)}` });
|
|
1598
|
+
return;
|
|
1599
|
+
case "set-draft":
|
|
1600
|
+
yield* updatePullRequestCli(input.cwd, id, ["--draft", action.draft ? "true" : "false"]);
|
|
1601
|
+
return;
|
|
1602
|
+
case "close":
|
|
1603
|
+
yield* updatePullRequestCli(input.cwd, id, ["--status", "abandoned"]);
|
|
1604
|
+
if (action.deleteBranch === true) yield* deleteSourceBranch(input.cwd, scope, pullRequest);
|
|
1605
|
+
return;
|
|
1606
|
+
case "reopen":
|
|
1607
|
+
yield* updatePullRequestCli(input.cwd, id, ["--status", "active"]);
|
|
1608
|
+
return;
|
|
1609
|
+
case "reviewers":
|
|
1610
|
+
for (const [verb, reviewers] of [["add", action.add], ["remove", action.remove]]) {
|
|
1611
|
+
if (reviewers.length === 0) continue;
|
|
1612
|
+
yield* executeJson(input.cwd, operation, [
|
|
1613
|
+
"repos",
|
|
1614
|
+
"pr",
|
|
1615
|
+
"reviewer",
|
|
1616
|
+
verb,
|
|
1617
|
+
"--detect",
|
|
1618
|
+
"true",
|
|
1619
|
+
"--id",
|
|
1620
|
+
String(id),
|
|
1621
|
+
"--reviewers",
|
|
1622
|
+
...reviewers
|
|
1623
|
+
]);
|
|
1624
|
+
}
|
|
1625
|
+
return;
|
|
1626
|
+
case "labels":
|
|
1627
|
+
for (const name of action.add) yield* azure.invoke({
|
|
1628
|
+
cwd: input.cwd,
|
|
1629
|
+
operation,
|
|
1630
|
+
area: "git",
|
|
1631
|
+
resource: "pullRequestLabels",
|
|
1632
|
+
routeParameters: route(scope),
|
|
1633
|
+
httpMethod: "POST",
|
|
1634
|
+
body: { name }
|
|
1635
|
+
});
|
|
1636
|
+
for (const name of action.remove) yield* azure.invoke({
|
|
1637
|
+
cwd: input.cwd,
|
|
1638
|
+
operation,
|
|
1639
|
+
area: "git",
|
|
1640
|
+
resource: "pullRequestLabels",
|
|
1641
|
+
routeParameters: route(scope, { labelIdOrName: name }),
|
|
1642
|
+
httpMethod: "DELETE"
|
|
1643
|
+
});
|
|
1644
|
+
return;
|
|
1645
|
+
case "auto-merge": {
|
|
1646
|
+
if (!action.enabled) {
|
|
1647
|
+
yield* updatePullRequestCli(input.cwd, id, ["--auto-complete", "false"]);
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
if (action.expectedHeadSha) yield* verifyHead(operation, pullRequest, action.expectedHeadSha);
|
|
1651
|
+
const viewer = yield* resolveViewer(input.cwd);
|
|
1652
|
+
if (!viewer) return yield* cliError(operation, "Could not resolve your Azure DevOps identity.");
|
|
1653
|
+
const deleteSourceBranchOption = pullRequest.completionOptions?.deleteSourceBranch;
|
|
1654
|
+
yield* patchPullRequest(input.cwd, operation, scope, {
|
|
1655
|
+
autoCompleteSetBy: { id: viewer.id },
|
|
1656
|
+
completionOptions: {
|
|
1657
|
+
mergeStrategy: azureDevOpsMergeStrategy(action.mergeMethod ?? "merge"),
|
|
1658
|
+
...deleteSourceBranchOption === true || deleteSourceBranchOption === "true" ? { deleteSourceBranch: true } : {}
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
case "delete-branch":
|
|
1664
|
+
yield* deleteSourceBranch(input.cwd, scope, pullRequest);
|
|
1665
|
+
return;
|
|
1666
|
+
case "assignees":
|
|
1667
|
+
case "update-branch": return yield* cliError(operation, action.kind === "assignees" ? "Azure DevOps pull requests have no assignees." : "Azure DevOps cannot update a branch from its base.");
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
const merge = (input) => Effect.gen(function* () {
|
|
1671
|
+
const operation = "mergeChangeRequest";
|
|
1672
|
+
const pullRequest = yield* readPullRequest(input.cwd, input.reference);
|
|
1673
|
+
const scope = yield* scopeOf(operation, pullRequest);
|
|
1674
|
+
if (pullRequest.status.trim().toLowerCase() !== "active") return yield* cliError(operation, "Only active pull requests can be completed.");
|
|
1675
|
+
if (input.expectedHeadSha) yield* verifyHead(operation, pullRequest, input.expectedHeadSha);
|
|
1676
|
+
const head = input.expectedHeadSha?.trim() ?? headOf(pullRequest);
|
|
1677
|
+
if (!head) return yield* cliError(operation, "Azure DevOps has not prepared this pull request's merge yet. Try again shortly.");
|
|
1678
|
+
return { outcome: (yield* patchPullRequest(input.cwd, operation, scope, {
|
|
1679
|
+
status: "completed",
|
|
1680
|
+
lastMergeSourceCommit: { commitId: head },
|
|
1681
|
+
completionOptions: {
|
|
1682
|
+
mergeStrategy: azureDevOpsMergeStrategy(input.mergeMethod),
|
|
1683
|
+
deleteSourceBranch: input.deleteBranch === true
|
|
1684
|
+
}
|
|
1685
|
+
})).status.trim().toLowerCase() === "completed" ? "merged" : "enqueued" };
|
|
1686
|
+
});
|
|
1687
|
+
const readRepositoryProject = (cwd, operation) => executeJson(cwd, operation, [
|
|
1688
|
+
"repos",
|
|
1689
|
+
"show",
|
|
1690
|
+
"--detect",
|
|
1691
|
+
"true"
|
|
1692
|
+
]).pipe(Effect.flatMap(decodeWith(operation, RepositorySchema)), Effect.flatMap((repository) => {
|
|
1693
|
+
const project = repository.project?.id?.trim() || repository.project?.name?.trim();
|
|
1694
|
+
return project ? Effect.succeed(project) : Effect.fail(cliError(operation, "Azure DevOps did not report the repository's project."));
|
|
1695
|
+
}));
|
|
1696
|
+
/** Members of the project's default team: the reviewer picker's candidates. */
|
|
1697
|
+
const listAssignees = (input) => Effect.gen(function* () {
|
|
1698
|
+
const operation = "listAssignees";
|
|
1699
|
+
const project = yield* readRepositoryProject(input.cwd, operation);
|
|
1700
|
+
const team = (yield* executeJson(input.cwd, operation, [
|
|
1701
|
+
"devops",
|
|
1702
|
+
"project",
|
|
1703
|
+
"show",
|
|
1704
|
+
"--detect",
|
|
1705
|
+
"true",
|
|
1706
|
+
"--project",
|
|
1707
|
+
project
|
|
1708
|
+
]).pipe(Effect.flatMap(decodeWith(operation, ProjectSchema)))).defaultTeam?.id;
|
|
1709
|
+
if (!team) return [];
|
|
1710
|
+
return (yield* executeJson(input.cwd, operation, [
|
|
1711
|
+
"devops",
|
|
1712
|
+
"team",
|
|
1713
|
+
"list-member",
|
|
1714
|
+
"--detect",
|
|
1715
|
+
"true",
|
|
1716
|
+
"--project",
|
|
1717
|
+
project,
|
|
1718
|
+
"--team",
|
|
1719
|
+
team,
|
|
1720
|
+
"--top",
|
|
1721
|
+
"500"
|
|
1722
|
+
]).pipe(Effect.flatMap(decodeWith(operation, TeamMemberList)))).flatMap((member) => {
|
|
1723
|
+
const login = azureDevOpsIdentityLogin(member.identity);
|
|
1724
|
+
if (!login || member.identity.isContainer === true) return [];
|
|
1725
|
+
const displayName = member.identity.displayName?.trim();
|
|
1726
|
+
const avatarUrl = member.identity.imageUrl?.trim();
|
|
1727
|
+
return [{
|
|
1728
|
+
login,
|
|
1729
|
+
...displayName && displayName !== login ? { displayName } : {},
|
|
1730
|
+
...avatarUrl ? { avatarUrl } : {}
|
|
1731
|
+
}];
|
|
1732
|
+
});
|
|
1733
|
+
});
|
|
1734
|
+
/** Active project tags (pull request labels share the project's tag definitions). */
|
|
1735
|
+
const listLabels = (input) => Effect.gen(function* () {
|
|
1736
|
+
const operation = "listLabels";
|
|
1737
|
+
const project = yield* readRepositoryProject(input.cwd, operation);
|
|
1738
|
+
return (yield* azure.invoke({
|
|
1739
|
+
cwd: input.cwd,
|
|
1740
|
+
operation,
|
|
1741
|
+
area: "wit",
|
|
1742
|
+
resource: "tags",
|
|
1743
|
+
routeParameters: { project }
|
|
1744
|
+
}).pipe(Effect.flatMap(decodeWith(operation, TagCollection)))).value.filter((tag) => tag.active !== false && tag.name.trim().length > 0).map((tag) => ({ name: tag.name.trim() }));
|
|
1745
|
+
});
|
|
1746
|
+
return {
|
|
1747
|
+
getDetail,
|
|
1748
|
+
getActivity,
|
|
1749
|
+
getDiff,
|
|
1750
|
+
getFileContents,
|
|
1751
|
+
listChangeRequests,
|
|
1752
|
+
addComment,
|
|
1753
|
+
replyToThread,
|
|
1754
|
+
setThreadResolved,
|
|
1755
|
+
updateComment,
|
|
1756
|
+
submitReview,
|
|
1757
|
+
updatePullRequest,
|
|
1758
|
+
merge,
|
|
1759
|
+
listAssignees,
|
|
1760
|
+
listLabels
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
/** Repository-relative path Ryco may read from git (no option or traversal injection). */
|
|
1764
|
+
function isRepositoryPath(path) {
|
|
1765
|
+
if (path.length === 0 || path.startsWith("/") || path.includes("\0")) return false;
|
|
1766
|
+
return path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
1767
|
+
}
|
|
1768
|
+
function toDetail(input) {
|
|
1769
|
+
const pullRequest = input.pullRequest;
|
|
1770
|
+
const record = normalizeAzureDevOpsPullRequestRecord(pullRequest);
|
|
1771
|
+
const reviewers = pullRequest.reviewers ?? [];
|
|
1772
|
+
const closedAt = pullRequest.closedDate && pullRequest.closedDate._tag === "Some" ? pullRequest.closedDate.value : null;
|
|
1773
|
+
const merged = record.state === "merged";
|
|
1774
|
+
const mergedBy = merged ? azureDevOpsIdentityLogin(pullRequest.closedBy) : null;
|
|
1775
|
+
const mergeStateStatus = azureDevOpsMergeStateStatus({
|
|
1776
|
+
pullRequest,
|
|
1777
|
+
evaluations: input.evaluations
|
|
1778
|
+
});
|
|
1779
|
+
const reviewerStates = azureDevOpsReviewerStates(reviewers);
|
|
1780
|
+
return {
|
|
1781
|
+
...record,
|
|
1782
|
+
body: pullRequest.description ?? "",
|
|
1783
|
+
comments: flattenAzureDevOpsThreadComments(input.threads),
|
|
1784
|
+
commits: input.commits.map(toAzureDevOpsChangeRequestCommit).toReversed(),
|
|
1785
|
+
reviewers: reviewerStates.map((reviewer) => reviewer.login),
|
|
1786
|
+
reviewerStates,
|
|
1787
|
+
reviewDecision: record.state === "open" ? azureDevOpsReviewDecision({
|
|
1788
|
+
reviewers,
|
|
1789
|
+
evaluations: input.evaluations
|
|
1790
|
+
}) : null,
|
|
1791
|
+
...mergeStateStatus ? { mergeStateStatus } : {},
|
|
1792
|
+
...record.state === "open" ? { mergeCapabilities: azureDevOpsMergeCapabilities(input.evaluations) } : {},
|
|
1793
|
+
autoMerge: record.state === "open" ? azureDevOpsAutoMerge(pullRequest) : null,
|
|
1794
|
+
...closedAt && record.state === "closed" ? { closedAt } : {},
|
|
1795
|
+
...closedAt && merged ? {
|
|
1796
|
+
mergedAt: closedAt,
|
|
1797
|
+
closedAt
|
|
1798
|
+
} : {},
|
|
1799
|
+
...mergedBy ? { mergedBy } : {}
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
//#endregion
|
|
1803
|
+
//#region src/sourceControl/AzureDevOpsSourceControlProvider.ts
|
|
1804
|
+
function providerError(operation, cause) {
|
|
1805
|
+
return new SourceControlProviderError({
|
|
1806
|
+
provider: "azure-devops",
|
|
1807
|
+
operation,
|
|
1808
|
+
detail: cause.detail,
|
|
1809
|
+
cause
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
function toChangeRequest(summary) {
|
|
1813
|
+
return {
|
|
1814
|
+
provider: "azure-devops",
|
|
1815
|
+
number: summary.number,
|
|
1816
|
+
title: summary.title,
|
|
1817
|
+
url: summary.url,
|
|
1818
|
+
baseRefName: summary.baseRefName,
|
|
1819
|
+
headRefName: summary.headRefName,
|
|
1820
|
+
state: summary.state,
|
|
1821
|
+
updatedAt: summary.updatedAt,
|
|
1822
|
+
isCrossRepository: summary.isCrossRepository ?? false,
|
|
1823
|
+
...summary.isDraft !== void 0 ? { isDraft: summary.isDraft } : {},
|
|
1824
|
+
...summary.author ? { author: summary.author } : {},
|
|
1825
|
+
...summary.labels && summary.labels.length > 0 ? { labels: summary.labels } : {},
|
|
1826
|
+
...summary.headSha ? { headSha: summary.headSha } : {},
|
|
1827
|
+
...summary.mergeability ? { mergeability: summary.mergeability } : {},
|
|
1828
|
+
...summary.createdAt ? { createdAt: summary.createdAt } : {},
|
|
1829
|
+
...summary.reviewDecision !== void 0 ? { reviewDecision: summary.reviewDecision } : {}
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
function toIssueSummary(raw) {
|
|
1833
|
+
return {
|
|
1834
|
+
provider: "azure-devops",
|
|
1835
|
+
number: raw.number,
|
|
1836
|
+
title: raw.title,
|
|
1837
|
+
url: raw.url,
|
|
1838
|
+
state: raw.state,
|
|
1839
|
+
...raw.author ? { author: raw.author } : {},
|
|
1840
|
+
updatedAt: raw.updatedAt.pipe(Option.map((s) => DateTime.fromDateUnsafe(new Date(s)))),
|
|
1841
|
+
labels: raw.labels.map((name) => ({ name }))
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
function toIssueDetail(raw, options) {
|
|
1845
|
+
const content = options.fullContent ? {
|
|
1846
|
+
body: raw.body,
|
|
1847
|
+
comments: raw.comments,
|
|
1848
|
+
truncated: false
|
|
1849
|
+
} : truncateSourceControlDetailContent({
|
|
1850
|
+
body: raw.body,
|
|
1851
|
+
comments: raw.comments
|
|
1852
|
+
});
|
|
1853
|
+
return {
|
|
1854
|
+
...toIssueSummary(raw),
|
|
1855
|
+
body: content.body,
|
|
1856
|
+
comments: content.comments.map((c) => ({
|
|
1857
|
+
author: c.author,
|
|
1858
|
+
body: c.body,
|
|
1859
|
+
createdAt: DateTime.fromDateUnsafe(new Date(c.createdAt))
|
|
1860
|
+
})),
|
|
1861
|
+
truncated: content.truncated
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
function toChangeRequestDetail(raw, options) {
|
|
1865
|
+
const content = options.fullContent ? {
|
|
1866
|
+
body: raw.body,
|
|
1867
|
+
comments: raw.comments,
|
|
1868
|
+
truncated: false
|
|
1869
|
+
} : truncateSourceControlDetailContent({
|
|
1870
|
+
body: raw.body,
|
|
1871
|
+
comments: raw.comments
|
|
1872
|
+
});
|
|
1873
|
+
return {
|
|
1874
|
+
...toChangeRequest(raw),
|
|
1875
|
+
body: content.body,
|
|
1876
|
+
comments: content.comments.map((c) => ({
|
|
1877
|
+
id: c.id,
|
|
1878
|
+
author: c.author,
|
|
1879
|
+
body: c.body,
|
|
1880
|
+
createdAt: DateTime.fromDateUnsafe(new Date(c.createdAt))
|
|
1881
|
+
})),
|
|
1882
|
+
truncated: content.truncated,
|
|
1883
|
+
...raw.commits.length > 0 ? { commits: raw.commits } : {},
|
|
1884
|
+
...raw.reviewers.length > 0 ? { reviewers: raw.reviewers } : {},
|
|
1885
|
+
reviewerStates: raw.reviewerStates,
|
|
1886
|
+
reviewDecision: raw.reviewDecision,
|
|
1887
|
+
...raw.mergeStateStatus ? { mergeStateStatus: raw.mergeStateStatus } : {},
|
|
1888
|
+
...raw.mergeCapabilities ? { mergeCapabilities: raw.mergeCapabilities } : {},
|
|
1889
|
+
autoMerge: raw.autoMerge,
|
|
1890
|
+
...raw.closedAt ? { closedAt: raw.closedAt } : {},
|
|
1891
|
+
...raw.mergedAt ? { mergedAt: raw.mergedAt } : {},
|
|
1892
|
+
...raw.mergedBy ? { mergedBy: raw.mergedBy } : {}
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
function remoteNameOf(context) {
|
|
1896
|
+
return context?.remoteName ?? "origin";
|
|
1897
|
+
}
|
|
1898
|
+
const make = Effect.fn("makeAzureDevOpsSourceControlProvider")(function* () {
|
|
1899
|
+
const azure = yield* AzureDevOpsCli;
|
|
1900
|
+
const page = makeAzureDevOpsPullRequestOperations(azure);
|
|
1901
|
+
const getChangeRequestDetail = (input) => page.getDetail({
|
|
1902
|
+
cwd: input.cwd,
|
|
1903
|
+
reference: input.reference
|
|
1904
|
+
}).pipe(Effect.map((raw) => toChangeRequestDetail(raw, { fullContent: input.fullContent ?? false })), Effect.mapError((error) => providerError("getChangeRequestDetail", error)));
|
|
1905
|
+
const freshDetail = (operation, input) => page.getDetail(input).pipe(Effect.map((raw) => toChangeRequestDetail(raw, { fullContent: true })), Effect.mapError((error) => providerError(operation, error)));
|
|
1906
|
+
const provider = SourceControlProvider.of({
|
|
1907
|
+
kind: "azure-devops",
|
|
1908
|
+
listChangeRequests: (input) => {
|
|
1909
|
+
const source = sourceControlRefFromInput(input);
|
|
1910
|
+
return page.listChangeRequests({
|
|
1911
|
+
cwd: input.cwd,
|
|
1912
|
+
headSelector: input.headSelector,
|
|
1913
|
+
...source ? { source } : {},
|
|
1914
|
+
state: input.state,
|
|
1915
|
+
...input.limit !== void 0 ? { limit: input.limit } : {},
|
|
1916
|
+
...input.involvement !== void 0 ? { involvement: input.involvement } : {},
|
|
1917
|
+
...input.query !== void 0 ? { query: input.query } : {}
|
|
1918
|
+
}).pipe(Effect.map((items) => items.map(toChangeRequest)), Effect.mapError((error) => providerError("listChangeRequests", error)));
|
|
1919
|
+
},
|
|
1920
|
+
getChangeRequest: (input) => azure.getPullRequest(input).pipe(Effect.map(toChangeRequest), Effect.mapError((error) => providerError("getChangeRequest", error))),
|
|
1921
|
+
createChangeRequest: (input) => {
|
|
1922
|
+
const source = sourceControlRefFromInput(input);
|
|
1923
|
+
return azure.createPullRequest({
|
|
1924
|
+
cwd: input.cwd,
|
|
1925
|
+
baseBranch: input.baseRefName,
|
|
1926
|
+
headSelector: input.headSelector,
|
|
1927
|
+
...source ? { source } : {},
|
|
1928
|
+
...input.target ? { target: input.target } : {},
|
|
1929
|
+
title: input.title,
|
|
1930
|
+
bodyFile: input.bodyFile,
|
|
1931
|
+
...input.draft ? { draft: true } : {}
|
|
1932
|
+
}).pipe(Effect.mapError((error) => providerError("createChangeRequest", error)));
|
|
1933
|
+
},
|
|
1934
|
+
getRepositoryCloneUrls: (input) => azure.getRepositoryCloneUrls(input).pipe(Effect.mapError((error) => providerError("getRepositoryCloneUrls", error))),
|
|
1935
|
+
createRepository: (input) => azure.createRepository(input).pipe(Effect.mapError((error) => providerError("createRepository", error))),
|
|
1936
|
+
getDefaultBranch: (input) => azure.getDefaultBranch({ cwd: input.cwd }).pipe(Effect.mapError((error) => providerError("getDefaultBranch", error))),
|
|
1937
|
+
checkoutChangeRequest: (input) => azure.checkoutPullRequest({
|
|
1938
|
+
cwd: input.cwd,
|
|
1939
|
+
reference: input.reference,
|
|
1940
|
+
...input.context ? { remoteName: input.context.remoteName } : {}
|
|
1941
|
+
}).pipe(Effect.mapError((error) => providerError("checkoutChangeRequest", error))),
|
|
1942
|
+
listIssues: (input) => azure.listWorkItems({
|
|
1943
|
+
cwd: input.cwd,
|
|
1944
|
+
state: input.state,
|
|
1945
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1946
|
+
}).pipe(Effect.map((items) => items.map(toIssueSummary)), Effect.mapError((error) => providerError("listIssues", error))),
|
|
1947
|
+
getIssue: (input) => azure.getWorkItem({
|
|
1948
|
+
cwd: input.cwd,
|
|
1949
|
+
reference: input.reference
|
|
1950
|
+
}).pipe(Effect.map((raw) => toIssueDetail(raw, { fullContent: input.fullContent ?? false })), Effect.mapError((error) => providerError("getIssue", error))),
|
|
1951
|
+
addIssueComment: () => Effect.fail(new SourceControlProviderError({
|
|
1952
|
+
provider: "azure-devops",
|
|
1953
|
+
operation: "addIssueComment",
|
|
1954
|
+
detail: "Not implemented for azure-devops"
|
|
1955
|
+
})),
|
|
1956
|
+
addIssueCommentReaction: () => Effect.fail(new SourceControlProviderError({
|
|
1957
|
+
provider: "azure-devops",
|
|
1958
|
+
operation: "addIssueCommentReaction",
|
|
1959
|
+
detail: "Not implemented for azure-devops"
|
|
1960
|
+
})),
|
|
1961
|
+
searchIssues: (input) => azure.searchWorkItems({
|
|
1962
|
+
cwd: input.cwd,
|
|
1963
|
+
query: input.query,
|
|
1964
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1965
|
+
}).pipe(Effect.map((items) => items.map(toIssueSummary)), Effect.mapError((error) => providerError("searchIssues", error))),
|
|
1966
|
+
searchChangeRequests: (input) => azure.searchPullRequests({
|
|
1967
|
+
cwd: input.cwd,
|
|
1968
|
+
query: input.query,
|
|
1969
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1970
|
+
}).pipe(Effect.map((items) => items.map(toChangeRequest)), Effect.mapError((error) => providerError("searchChangeRequests", error))),
|
|
1971
|
+
getChangeRequestDetail,
|
|
1972
|
+
addChangeRequestComment: (input) => page.addComment({
|
|
1973
|
+
cwd: input.cwd,
|
|
1974
|
+
reference: input.reference,
|
|
1975
|
+
body: input.body
|
|
1976
|
+
}).pipe(Effect.mapError((error) => providerError("addChangeRequestComment", error)), Effect.flatMap(() => freshDetail("addChangeRequestComment", input))),
|
|
1977
|
+
addChangeRequestCommentReaction: () => Effect.fail(new SourceControlProviderError({
|
|
1978
|
+
provider: "azure-devops",
|
|
1979
|
+
operation: "addChangeRequestCommentReaction",
|
|
1980
|
+
detail: "Azure DevOps does not support comment reactions."
|
|
1981
|
+
})),
|
|
1982
|
+
getChangeRequestDiff: (input) => page.getDiff({
|
|
1983
|
+
cwd: input.cwd,
|
|
1984
|
+
reference: input.reference,
|
|
1985
|
+
remoteName: remoteNameOf(input.context),
|
|
1986
|
+
...input.expectedHeadSha ? { expectedHeadSha: input.expectedHeadSha } : {},
|
|
1987
|
+
...input.commitSha ? { commitSha: input.commitSha } : {}
|
|
1988
|
+
}).pipe(Effect.mapError((error) => providerError("getChangeRequestDiff", error))),
|
|
1989
|
+
getChangeRequestActivity: (input) => page.getActivity({
|
|
1990
|
+
cwd: input.cwd,
|
|
1991
|
+
reference: input.reference
|
|
1992
|
+
}).pipe(Effect.mapError((error) => providerError("getChangeRequestActivity", error))),
|
|
1993
|
+
getChangeRequestFileContents: (input) => page.getFileContents({
|
|
1994
|
+
cwd: input.cwd,
|
|
1995
|
+
reference: input.reference,
|
|
1996
|
+
remoteName: remoteNameOf(input.context),
|
|
1997
|
+
path: input.path,
|
|
1998
|
+
...input.previousPath ? { previousPath: input.previousPath } : {},
|
|
1999
|
+
...input.baseSha ? { baseSha: input.baseSha } : {},
|
|
2000
|
+
headSha: input.headSha
|
|
2001
|
+
}).pipe(Effect.mapError((error) => providerError("getChangeRequestFileContents", error))),
|
|
2002
|
+
submitChangeRequestReview: ({ context: _context, ...input }) => page.submitReview(input).pipe(Effect.mapError((error) => providerError("submitChangeRequestReview", error))),
|
|
2003
|
+
replyToReviewThread: (input) => page.replyToThread({
|
|
2004
|
+
cwd: input.cwd,
|
|
2005
|
+
reference: input.reference,
|
|
2006
|
+
threadId: input.threadId,
|
|
2007
|
+
body: input.body
|
|
2008
|
+
}).pipe(Effect.map((thread) => ({ thread })), Effect.mapError((error) => providerError("replyToReviewThread", error))),
|
|
2009
|
+
setReviewThreadResolved: (input) => page.setThreadResolved({
|
|
2010
|
+
cwd: input.cwd,
|
|
2011
|
+
reference: input.reference,
|
|
2012
|
+
threadId: input.threadId,
|
|
2013
|
+
resolved: input.resolved
|
|
2014
|
+
}).pipe(Effect.mapError((error) => providerError("setReviewThreadResolved", error))),
|
|
2015
|
+
updateChangeRequestComment: ({ context: _context, ...input }) => page.updateComment(input).pipe(Effect.mapError((error) => providerError("updateChangeRequestComment", error))),
|
|
2016
|
+
updateChangeRequest: (input) => page.updatePullRequest({
|
|
2017
|
+
cwd: input.cwd,
|
|
2018
|
+
reference: input.reference,
|
|
2019
|
+
action: input.action
|
|
2020
|
+
}).pipe(Effect.mapError((error) => providerError("updateChangeRequest", error)), Effect.flatMap(() => freshDetail("updateChangeRequest", input)), Effect.map((detail) => ({ detail }))),
|
|
2021
|
+
mergeChangeRequest: (input) => page.merge({
|
|
2022
|
+
cwd: input.cwd,
|
|
2023
|
+
reference: input.reference,
|
|
2024
|
+
mergeMethod: input.mergeMethod,
|
|
2025
|
+
...input.deleteBranch !== void 0 ? { deleteBranch: input.deleteBranch } : {},
|
|
2026
|
+
...input.expectedHeadSha ? { expectedHeadSha: input.expectedHeadSha } : {}
|
|
2027
|
+
}).pipe(Effect.mapError((error) => providerError("mergeChangeRequest", error))),
|
|
2028
|
+
createIssue: () => Effect.fail(new SourceControlProviderError({
|
|
2029
|
+
provider: "azure-devops",
|
|
2030
|
+
operation: "createIssue",
|
|
2031
|
+
detail: "Not implemented in Phase 1"
|
|
2032
|
+
})),
|
|
2033
|
+
listLabels: (input) => page.listLabels({ cwd: input.cwd }).pipe(Effect.mapError((error) => providerError("listLabels", error))),
|
|
2034
|
+
listAssignees: (input) => page.listAssignees({ cwd: input.cwd }).pipe(Effect.mapError((error) => providerError("listAssignees", error))),
|
|
2035
|
+
getPullRequestState: (input) => azure.getPullRequest({
|
|
2036
|
+
cwd: input.cwd,
|
|
2037
|
+
reference: String(input.number)
|
|
2038
|
+
}).pipe(Effect.map((summary) => ({
|
|
2039
|
+
state: summary.state,
|
|
2040
|
+
isDraft: summary.isDraft ?? false,
|
|
2041
|
+
terminalAt: summary.state === "open" ? null : summary.closedAt ?? null
|
|
2042
|
+
})), Effect.mapError((error) => providerError("getPullRequestState", error))),
|
|
2043
|
+
getIssueState: () => Effect.fail(new SourceControlProviderError({
|
|
2044
|
+
provider: "azure-devops",
|
|
2045
|
+
operation: "getIssueState",
|
|
2046
|
+
detail: "Not implemented for azure-devops"
|
|
2047
|
+
}))
|
|
2048
|
+
});
|
|
2049
|
+
return withUnsupportedChangeRequestOptionGuards(provider);
|
|
2050
|
+
});
|
|
2051
|
+
const layer = Layer.effect(SourceControlProvider, make());
|
|
2052
|
+
//#endregion
|
|
2053
|
+
export { azureDevOpsDiscovery as discovery, layer, make };
|