@cursor/july 0.1.102 → 0.1.103
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/AGENTS.md +34 -32
- package/README.md +10 -12
- package/dist/ab.d.ts +1 -1
- package/dist/ab.js +1 -1
- package/dist/bin/agent-serve.d.ts +12 -12
- package/dist/bin/agent-serve.js +114 -36
- package/dist/channels/bitbucket/api.d.ts +176 -0
- package/dist/channels/bitbucket/api.d.ts.map +1 -0
- package/dist/channels/bitbucket/api.js +419 -0
- package/dist/channels/bitbucket/binding.d.ts +71 -0
- package/dist/channels/bitbucket/binding.d.ts.map +1 -0
- package/dist/channels/bitbucket/binding.js +269 -0
- package/dist/channels/bitbucket/bitbucket-channel.d.ts +33 -0
- package/dist/channels/bitbucket/bitbucket-channel.d.ts.map +1 -0
- package/dist/channels/bitbucket/bitbucket-channel.js +120 -0
- package/dist/channels/bitbucket/bitbucket-webhook.d.ts +61 -0
- package/dist/channels/bitbucket/bitbucket-webhook.d.ts.map +1 -0
- package/dist/channels/bitbucket/bitbucket-webhook.js +394 -0
- package/dist/channels/bitbucket/checks.d.ts +67 -0
- package/dist/channels/bitbucket/checks.d.ts.map +1 -0
- package/dist/channels/bitbucket/checks.js +102 -0
- package/dist/channels/bitbucket/cursor-account.d.ts +37 -0
- package/dist/channels/bitbucket/cursor-account.d.ts.map +1 -0
- package/dist/channels/bitbucket/cursor-account.js +65 -0
- package/dist/channels/bitbucket/defaults.d.ts +26 -0
- package/dist/channels/bitbucket/defaults.d.ts.map +1 -0
- package/dist/channels/bitbucket/defaults.js +73 -0
- package/dist/channels/bitbucket/events.d.ts +22 -0
- package/dist/channels/bitbucket/events.d.ts.map +1 -0
- package/dist/channels/bitbucket/events.js +64 -0
- package/dist/channels/bitbucket/index.d.ts +34 -0
- package/dist/channels/bitbucket/index.d.ts.map +1 -0
- package/dist/channels/bitbucket/index.js +33 -0
- package/dist/channels/bitbucket/replay.d.ts +63 -0
- package/dist/channels/bitbucket/replay.d.ts.map +1 -0
- package/dist/channels/bitbucket/replay.js +304 -0
- package/dist/channels/bitbucket/state.d.ts +35 -0
- package/dist/channels/bitbucket/state.d.ts.map +1 -0
- package/dist/channels/bitbucket/state.js +21 -0
- package/dist/channels/bitbucket/types.d.ts +229 -0
- package/dist/channels/bitbucket/types.d.ts.map +1 -0
- package/dist/channels/bitbucket/types.js +1 -0
- package/dist/channels/deployments/deployments-channel.d.ts +22 -14
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +25 -27
- package/dist/channels/deployments/types.d.ts +12 -6
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/github/binding.d.ts +6 -0
- package/dist/channels/github/binding.d.ts.map +1 -1
- package/dist/channels/github/binding.js +3 -3
- package/dist/channels/github/cursor-account.d.ts +7 -3
- package/dist/channels/github/cursor-account.d.ts.map +1 -1
- package/dist/channels/github/cursor-account.js +10 -6
- package/dist/channels/github/events.d.ts +1 -1
- package/dist/channels/github/events.js +1 -1
- package/dist/channels/github/forward.d.ts +1 -1
- package/dist/channels/github/forward.d.ts.map +1 -1
- package/dist/channels/github/forward.js +3 -2
- package/dist/channels/github/github-channel.d.ts +29 -39
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +68 -101
- package/dist/channels/github/replay.d.ts +1 -1
- package/dist/channels/github/replay.js +1 -1
- package/dist/channels/github/state.d.ts +2 -0
- package/dist/channels/github/state.d.ts.map +1 -1
- package/dist/channels/github/state.js +14 -11
- package/dist/channels/github/types.d.ts +2 -4
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/gitlab/api.d.ts +91 -0
- package/dist/channels/gitlab/api.d.ts.map +1 -0
- package/dist/channels/gitlab/api.js +150 -0
- package/dist/channels/gitlab/binding.d.ts +58 -0
- package/dist/channels/gitlab/binding.d.ts.map +1 -0
- package/dist/channels/gitlab/binding.js +139 -0
- package/dist/channels/gitlab/checks.d.ts +110 -0
- package/dist/channels/gitlab/checks.d.ts.map +1 -0
- package/dist/channels/gitlab/checks.js +126 -0
- package/dist/channels/gitlab/cursor-account.d.ts +36 -0
- package/dist/channels/gitlab/cursor-account.d.ts.map +1 -0
- package/dist/channels/gitlab/cursor-account.js +65 -0
- package/dist/channels/gitlab/defaults.d.ts +25 -0
- package/dist/channels/gitlab/defaults.d.ts.map +1 -0
- package/dist/channels/gitlab/defaults.js +60 -0
- package/dist/channels/gitlab/events.d.ts +12 -0
- package/dist/channels/gitlab/events.d.ts.map +1 -0
- package/dist/channels/gitlab/events.js +28 -0
- package/dist/channels/gitlab/gitlab-channel.d.ts +33 -0
- package/dist/channels/gitlab/gitlab-channel.d.ts.map +1 -0
- package/dist/channels/gitlab/gitlab-channel.js +120 -0
- package/dist/channels/gitlab/gitlab-webhook.d.ts +45 -0
- package/dist/channels/gitlab/gitlab-webhook.d.ts.map +1 -0
- package/dist/channels/gitlab/gitlab-webhook.js +397 -0
- package/dist/channels/gitlab/index.d.ts +31 -0
- package/dist/channels/gitlab/index.d.ts.map +1 -0
- package/dist/channels/gitlab/index.js +30 -0
- package/dist/channels/gitlab/replay.d.ts +105 -0
- package/dist/channels/gitlab/replay.d.ts.map +1 -0
- package/dist/channels/gitlab/replay.js +206 -0
- package/dist/channels/gitlab/state.d.ts +33 -0
- package/dist/channels/gitlab/state.d.ts.map +1 -0
- package/dist/channels/gitlab/state.js +23 -0
- package/dist/channels/gitlab/types.d.ts +214 -0
- package/dist/channels/gitlab/types.d.ts.map +1 -0
- package/dist/channels/gitlab/types.js +1 -0
- package/dist/channels/origin/api.d.ts.map +1 -1
- package/dist/channels/origin/api.js +4 -2
- package/dist/channels/origin/origin-channel.d.ts +4 -9
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +8 -48
- package/dist/channels/origin/types.d.ts +20 -2
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/eval-directive.d.ts.map +1 -1
- package/dist/channels/slack/eval-directive.js +1 -7
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +3 -3
- package/dist/channels/slack/setup.d.ts +1 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +6 -13
- package/dist/channels/slack/types.d.ts +1 -1
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/channels.d.ts +1 -1
- package/dist/channels.d.ts.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/connections.d.ts +1 -1
- package/dist/connections.js +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CLA03RCz.js → app.DW9pXkpL.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.ifxv-cJV.js → VPLocalSearchBox.CdEIUwDg.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DWCwM6p-.js → theme.YZMHgPgg.js} +2 -2
- package/dist/docs/assets/concepts.md.DgEcZOfT.js +1 -0
- package/dist/docs/assets/{concepts.md.rPzW2HZN.lean.js → concepts.md.DgEcZOfT.lean.js} +1 -1
- package/dist/docs/assets/{evals.md.CPzDAwoH.js → evals.md.CbMoebP1.js} +1 -1
- package/dist/docs/assets/guides_cloud-agents.md.DS8RIjwx.js +9 -0
- package/dist/docs/assets/{guides_github.md.BtPr9GaP.js → guides_github.md.DKYiT9do.js} +1 -1
- package/dist/docs/assets/{guides_opentelemetry.md.BVTXDCRg.js → guides_opentelemetry.md.COh9jFVg.js} +7 -8
- package/dist/docs/assets/{guides_opentelemetry.md.BVTXDCRg.lean.js → guides_opentelemetry.md.COh9jFVg.lean.js} +1 -1
- package/dist/docs/assets/{index.md.Kiht7ell.js → index.md.D75GWhPl.js} +1 -1
- package/dist/docs/assets/{reference_agent-config.md.BHeYMgme.js → reference_agent-config.md.CHNpiyp4.js} +3 -3
- package/dist/docs/assets/{reference_channels.md.Cd2f2iyV.js → reference_channels.md.Dz8Cnudp.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.Cd2f2iyV.lean.js → reference_channels.md.Dz8Cnudp.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.DfoeyvL0.js → reference_cli.md.CJNc_xYK.js} +1 -1
- package/dist/docs/assets/{reference_extensions.md.9WQMZe1l.js → reference_extensions.md.DykHW3qL.js} +11 -5
- package/dist/docs/assets/reference_extensions.md.DykHW3qL.lean.js +1 -0
- package/dist/docs/assets/reference_hooks.md.Ddt5DdgJ.js +73 -0
- package/dist/docs/assets/{reference_hooks.md.a8BJxMR5.lean.js → reference_hooks.md.Ddt5DdgJ.lean.js} +1 -1
- package/dist/docs/assets/{reference_playground.md.DnX5nL-B.js → reference_playground.md.4myJPxrf.js} +1 -1
- package/dist/docs/assets/{reference_project-layout.md.CA6QP6Lw.js → reference_project-layout.md.DuBu9a96.js} +2 -2
- package/dist/docs/assets/reference_result.md.BK5SxDrf.js +19 -0
- package/dist/docs/assets/reference_result.md.BK5SxDrf.lean.js +1 -0
- package/dist/docs/assets/{reference_sessions.md.NxFP_CAC.js → reference_sessions.md.CueyOHSL.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.NxFP_CAC.lean.js → reference_sessions.md.CueyOHSL.lean.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.B1up2r9W.js → reference_tools.md.aGrn99hP.js} +1 -1
- package/dist/docs/assets/skills_framework-map.md.haibFyoB.js +1 -0
- package/dist/docs/assets/{skills_otel.md.CgiZryR3.js → skills_otel.md.CqhobH3B.js} +3 -3
- package/dist/docs/assets/{skills_otel.md.CgiZryR3.lean.js → skills_otel.md.CqhobH3B.lean.js} +1 -1
- package/dist/docs/assets/{templates_pr-autofixer.md.x5zl6-GT.js → templates_pr-autofixer.md.Bds_XWcV.js} +2 -2
- package/dist/docs/assets/{templates_pr-autofixer.md.x5zl6-GT.lean.js → templates_pr-autofixer.md.Bds_XWcV.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/concepts.md +2 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/design/runtime-abstraction.md +2 -3
- package/dist/docs/evals.html +6 -6
- package/dist/docs/evals.md +9 -7
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-agents.html +6 -6
- package/dist/docs/guides/cloud-agents.md +4 -6
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/github.md +0 -1
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +5 -5
- package/dist/docs/guides/opentelemetry.html +10 -11
- package/dist/docs/guides/opentelemetry.md +41 -17
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +2 -2
- package/dist/docs/llms-full.txt +501 -262
- package/dist/docs/llms.txt +2 -2
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +8 -8
- package/dist/docs/reference/agent-config.md +6 -10
- package/dist/docs/reference/artifacts.html +5 -5
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/channels.md +18 -8
- package/dist/docs/reference/cli.html +5 -5
- package/dist/docs/reference/cli.md +3 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/extensions.html +16 -10
- package/dist/docs/reference/extensions.md +86 -22
- package/dist/docs/reference/hooks.html +69 -10
- package/dist/docs/reference/hooks.md +215 -46
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +5 -5
- package/dist/docs/reference/playground.md +3 -0
- package/dist/docs/reference/project-layout.html +6 -6
- package/dist/docs/reference/project-layout.md +2 -1
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/result.html +45 -0
- package/dist/docs/reference/result.md +89 -0
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/sessions.md +3 -1
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +5 -5
- package/dist/docs/reference/tools.md +1 -2
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/skills/ab.html +4 -4
- package/dist/docs/skills/create-agent.html +4 -4
- package/dist/docs/skills/debug.html +4 -4
- package/dist/docs/skills/evals.html +4 -4
- package/dist/docs/skills/framework-map.html +5 -5
- package/dist/docs/skills/framework-map.md +3 -1
- package/dist/docs/skills/github.html +4 -4
- package/dist/docs/skills/hillclimb.html +4 -4
- package/dist/docs/skills/index.html +4 -4
- package/dist/docs/skills/mcp-auth.html +4 -4
- package/dist/docs/skills/otel.html +7 -7
- package/dist/docs/skills/otel.md +8 -3
- package/dist/docs/skills/setup-slack.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +4 -4
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +6 -6
- package/dist/docs/templates/pr-autofixer.md +0 -3
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/evals.d.ts +1 -1
- package/dist/evals.d.ts.map +1 -1
- package/dist/extensions.d.ts +20 -7
- package/dist/extensions.d.ts.map +1 -1
- package/dist/extensions.js +36 -5
- package/dist/files.d.ts +16 -3
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +16 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/internal/ab-snapshot.d.ts.map +1 -1
- package/dist/internal/ab-snapshot.js +0 -1
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +1 -3
- package/dist/internal/authored-alias-hooks.d.ts +4 -13
- package/dist/internal/authored-alias-hooks.d.ts.map +1 -1
- package/dist/internal/authored-alias-hooks.js +4 -20
- package/dist/internal/authored-loaders.d.ts +2 -3
- package/dist/internal/authored-loaders.d.ts.map +1 -1
- package/dist/internal/authored-loaders.js +6 -20
- package/dist/internal/channel-dispatch.d.ts +43 -0
- package/dist/internal/channel-dispatch.d.ts.map +1 -0
- package/dist/internal/channel-dispatch.js +77 -0
- package/dist/internal/chat-client.d.ts +1 -1
- package/dist/internal/chat-client.js +1 -1
- package/dist/internal/cli-ax.d.ts +5 -5
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +5 -5
- package/dist/internal/cli-bitbucket.d.ts +33 -0
- package/dist/internal/cli-bitbucket.d.ts.map +1 -0
- package/dist/internal/cli-bitbucket.js +359 -0
- package/dist/internal/cli-convert-automation.d.ts +1 -1
- package/dist/internal/cli-convert-automation.js +2 -2
- package/dist/internal/cli-cursor.d.ts +1 -1
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +2 -4
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +2 -6
- package/dist/internal/cli-github.d.ts +1 -1
- package/dist/internal/cli-github.js +3 -3
- package/dist/internal/cli-gitlab.d.ts +37 -0
- package/dist/internal/cli-gitlab.d.ts.map +1 -0
- package/dist/internal/cli-gitlab.js +380 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +2 -6
- package/dist/internal/cli-slack.d.ts +1 -1
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +2 -4
- package/dist/internal/continuation-channel.d.ts +14 -0
- package/dist/internal/continuation-channel.d.ts.map +1 -1
- package/dist/internal/continuation-channel.js +56 -3
- package/dist/internal/continuation-identity.d.ts +14 -15
- package/dist/internal/continuation-identity.d.ts.map +1 -1
- package/dist/internal/continuation-identity.js +182 -7
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +4 -3
- package/dist/internal/convert-automation/types.d.ts +1 -1
- package/dist/internal/convert-automation/types.js +1 -1
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +1 -3
- package/dist/internal/cursor/backend-client.d.ts +5 -4
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +5 -6
- package/dist/internal/cursor/bound-secret-file.d.ts +56 -0
- package/dist/internal/cursor/bound-secret-file.d.ts.map +1 -0
- package/dist/internal/cursor/bound-secret-file.js +88 -0
- package/dist/internal/cursor/credentials.d.ts +39 -5
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +58 -8
- package/dist/internal/cursor/hosted-bound-secrets.d.ts +41 -0
- package/dist/internal/cursor/hosted-bound-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-bound-secrets.js +54 -0
- package/dist/internal/cursor/hosted-deployment-secrets.d.ts +19 -5
- package/dist/internal/cursor/hosted-deployment-secrets.d.ts.map +1 -1
- package/dist/internal/cursor/hosted-deployment-secrets.js +29 -9
- package/dist/internal/cursor/hosted-store-secrets.d.ts +1 -1
- package/dist/internal/cursor/hosted-store-secrets.js +1 -1
- package/dist/internal/cursor-account-mcp-auth.d.ts.map +1 -1
- package/dist/internal/cursor-account-mcp-auth.js +2 -4
- package/dist/internal/cursor-deployment-relay.d.ts +6 -2
- package/dist/internal/cursor-deployment-relay.d.ts.map +1 -1
- package/dist/internal/cursor-deployment-relay.js +13 -9
- package/dist/internal/deploy-manifest.d.ts +1 -1
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +1 -1
- package/dist/internal/discovery/ab.d.ts +1 -1
- package/dist/internal/discovery/ab.d.ts.map +1 -1
- package/dist/internal/discovery/ab.js +21 -9
- package/dist/internal/discovery/agent.d.ts +6 -1
- package/dist/internal/discovery/agent.d.ts.map +1 -1
- package/dist/internal/discovery/agent.js +65 -116
- package/dist/internal/discovery/channels.d.ts +1 -1
- package/dist/internal/discovery/channels.d.ts.map +1 -1
- package/dist/internal/discovery/channels.js +14 -14
- package/dist/internal/discovery/connections.d.ts +1 -5
- package/dist/internal/discovery/connections.d.ts.map +1 -1
- package/dist/internal/discovery/connections.js +30 -44
- package/dist/internal/discovery/cursor-plugin.d.ts.map +1 -1
- package/dist/internal/discovery/cursor-plugin.js +1 -3
- package/dist/internal/discovery/extension-overlay.d.ts +32 -0
- package/dist/internal/discovery/extension-overlay.d.ts.map +1 -0
- package/dist/internal/discovery/extension-overlay.js +191 -0
- package/dist/internal/discovery/extensions.d.ts +11 -2
- package/dist/internal/discovery/extensions.d.ts.map +1 -1
- package/dist/internal/discovery/extensions.js +211 -74
- package/dist/internal/discovery/hooks.d.ts +1 -1
- package/dist/internal/discovery/hooks.d.ts.map +1 -1
- package/dist/internal/discovery/hooks.js +15 -2
- package/dist/internal/discovery/info.d.ts.map +1 -1
- package/dist/internal/discovery/info.js +7 -1
- package/dist/internal/discovery/modules.d.ts.map +1 -1
- package/dist/internal/discovery/modules.js +8 -2
- package/dist/internal/discovery/names.d.ts +1 -1
- package/dist/internal/discovery/names.d.ts.map +1 -1
- package/dist/internal/discovery/names.js +1 -0
- package/dist/internal/discovery/project.d.ts +0 -2
- package/dist/internal/discovery/project.d.ts.map +1 -1
- package/dist/internal/discovery/project.js +64 -10
- package/dist/internal/discovery/runtime.d.ts.map +1 -1
- package/dist/internal/discovery/runtime.js +1 -2
- package/dist/internal/discovery/schedules.d.ts +1 -1
- package/dist/internal/discovery/schedules.d.ts.map +1 -1
- package/dist/internal/discovery/schedules.js +23 -2
- package/dist/internal/discovery/services.d.ts +6 -2
- package/dist/internal/discovery/services.d.ts.map +1 -1
- package/dist/internal/discovery/services.js +31 -1
- package/dist/internal/discovery/skills.d.ts +1 -6
- package/dist/internal/discovery/skills.d.ts.map +1 -1
- package/dist/internal/discovery/skills.js +8 -3
- package/dist/internal/discovery/subagent.d.ts +3 -0
- package/dist/internal/discovery/subagent.d.ts.map +1 -1
- package/dist/internal/discovery/subagent.js +109 -0
- package/dist/internal/discovery.d.ts +1 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +1 -2
- package/dist/internal/distribution.d.ts +10 -27
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +25 -73
- package/dist/internal/docs-site.js +1 -1
- package/dist/internal/eval-run-store.d.ts +0 -1
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.d.ts +8 -0
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +8 -8
- package/dist/internal/github-fanout.d.ts +1 -1
- package/dist/internal/github-fanout.js +1 -1
- package/dist/internal/guest-network.d.ts +101 -0
- package/dist/internal/guest-network.d.ts.map +1 -0
- package/dist/internal/guest-network.js +304 -0
- package/dist/internal/host-otel.d.ts +3 -3
- package/dist/internal/host-otel.d.ts.map +1 -1
- package/dist/internal/host-otel.js +8 -4
- package/dist/internal/hosted-admission-adapter.d.ts +8 -7
- package/dist/internal/hosted-admission-adapter.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.d.ts +6 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +18 -5
- package/dist/internal/hosted-delivery.d.ts +18 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +248 -118
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +1 -3
- package/dist/internal/init-interview.d.ts.map +1 -1
- package/dist/internal/init-interview.js +1 -3
- package/dist/internal/init-project.d.ts +2 -2
- package/dist/internal/init-project.js +2 -2
- package/dist/internal/inline-stdio-define-stub.d.ts +1 -1
- package/dist/internal/inline-stdio-define-stub.d.ts.map +1 -1
- package/dist/internal/inline-stdio-define-stub.js +1 -1
- package/dist/internal/is-record.d.ts +2 -0
- package/dist/internal/is-record.d.ts.map +1 -0
- package/dist/internal/is-record.js +3 -0
- package/dist/internal/json-schema.d.ts.map +1 -1
- package/dist/internal/json-schema.js +18 -10
- package/dist/internal/local-control-plane.d.ts +1 -2
- package/dist/internal/local-control-plane.d.ts.map +1 -1
- package/dist/internal/local-control-plane.js +3 -20
- package/dist/internal/mcp-endpoint.d.ts.map +1 -1
- package/dist/internal/mcp-endpoint.js +1 -3
- package/dist/internal/otel/collector.d.ts +12 -3
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +77 -125
- package/dist/internal/otel/config.d.ts +0 -2
- package/dist/internal/otel/config.d.ts.map +1 -1
- package/dist/internal/otel/config.js +17 -20
- package/dist/internal/otel/http.d.ts +2 -0
- package/dist/internal/otel/http.d.ts.map +1 -1
- package/dist/internal/otel/http.js +7 -1
- package/dist/internal/otel/mcp.d.ts.map +1 -1
- package/dist/internal/otel/mcp.js +3 -16
- package/dist/internal/otel/runtime.d.ts +26 -1
- package/dist/internal/otel/runtime.d.ts.map +1 -1
- package/dist/internal/otel/runtime.js +164 -15
- package/dist/internal/package-inline-stdio.d.ts.map +1 -1
- package/dist/internal/package-inline-stdio.js +3 -11
- package/dist/internal/playground/toolchain.d.ts +1 -1
- package/dist/internal/playground/toolchain.d.ts.map +1 -1
- package/dist/internal/playground/toolchain.js +1 -1
- package/dist/internal/request-error-status.d.ts +12 -0
- package/dist/internal/request-error-status.d.ts.map +1 -0
- package/dist/internal/request-error-status.js +52 -0
- package/dist/internal/resolve-prod-target.d.ts +1 -1
- package/dist/internal/resolve-prod-target.d.ts.map +1 -1
- package/dist/internal/resolve-prod-target.js +3 -7
- package/dist/internal/result-output.d.ts +18 -0
- package/dist/internal/result-output.d.ts.map +1 -0
- package/dist/internal/result-output.js +33 -0
- package/dist/internal/run-client.d.ts +2 -2
- package/dist/internal/run-client.js +2 -2
- package/dist/internal/runtime-dispatch-runner.d.ts +1 -11
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
- package/dist/internal/runtime-dispatch-runner.js +2 -15
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +3 -6
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +110 -35
- package/dist/internal/session-cost.d.ts +3 -3
- package/dist/internal/session-cost.d.ts.map +1 -1
- package/dist/internal/session-cost.js +3 -3
- package/dist/internal/session-engine.d.ts +26 -11
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +146 -76
- package/dist/internal/session-run-log.d.ts.map +1 -1
- package/dist/internal/session-run-log.js +1 -3
- package/dist/internal/skills-sync.d.ts +1 -4
- package/dist/internal/skills-sync.d.ts.map +1 -1
- package/dist/internal/skills-sync.js +10 -50
- package/dist/internal/sleep.d.ts +2 -0
- package/dist/internal/sleep.d.ts.map +1 -0
- package/dist/internal/sleep.js +3 -0
- package/dist/internal/store-mirror.d.ts +29 -0
- package/dist/internal/store-mirror.d.ts.map +1 -0
- package/dist/internal/store-mirror.js +70 -0
- package/dist/internal/tenant-slack/relay.d.ts +73 -0
- package/dist/internal/tenant-slack/relay.d.ts.map +1 -0
- package/dist/internal/tenant-slack/relay.js +138 -0
- package/dist/internal/testing/scm-continuation-fixtures.d.ts +33 -0
- package/dist/internal/testing/scm-continuation-fixtures.d.ts.map +1 -0
- package/dist/internal/testing/scm-continuation-fixtures.js +253 -0
- package/dist/internal/workspace-sync.d.ts +22 -0
- package/dist/internal/workspace-sync.d.ts.map +1 -0
- package/dist/internal/workspace-sync.js +49 -0
- package/dist/internal/workspace.d.ts +7 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +13 -0
- package/dist/otel.d.ts +23 -1
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +93 -2
- package/dist/playground/assets/index-59KvgREd.js +67 -0
- package/dist/playground/assets/index-MbMxXg-y.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/result.d.ts +24 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +27 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +7 -4
- package/dist/storage-protocol.d.ts.map +1 -1
- package/dist/storage-protocol.js +1 -3
- package/dist/types.d.ts +90 -23
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +2 -2
- package/docs/concepts.md +2 -4
- package/docs/design/runtime-abstraction.md +2 -3
- package/docs/evals.md +9 -7
- package/docs/guides/cloud-agents.md +4 -6
- package/docs/guides/github.md +0 -1
- package/docs/guides/opentelemetry.md +41 -17
- package/docs/reference/agent-config.md +6 -10
- package/docs/reference/channels.md +18 -8
- package/docs/reference/cli.md +3 -4
- package/docs/reference/extensions.md +87 -23
- package/docs/reference/hooks.md +215 -46
- package/docs/reference/playground.md +3 -0
- package/docs/reference/project-layout.md +2 -1
- package/docs/reference/result.md +94 -0
- package/docs/reference/sessions.md +3 -1
- package/docs/reference/tools.md +1 -2
- package/docs/templates/pr-autofixer.md +0 -3
- package/package.json +29 -10
- package/skills/framework-map/SKILL.md +3 -1
- package/skills/otel/SKILL.md +8 -3
- package/src/ab.ts +1 -1
- package/src/bin/agent-serve.ts +121 -21
- package/src/channels/bitbucket/api.ts +623 -0
- package/src/channels/bitbucket/binding.ts +414 -0
- package/src/channels/bitbucket/bitbucket-channel.ts +182 -0
- package/src/channels/bitbucket/bitbucket-webhook.ts +576 -0
- package/src/channels/bitbucket/checks.ts +154 -0
- package/src/channels/bitbucket/cursor-account.ts +117 -0
- package/src/channels/bitbucket/defaults.ts +110 -0
- package/src/channels/bitbucket/events.ts +84 -0
- package/src/channels/bitbucket/index.ts +49 -0
- package/src/channels/bitbucket/replay.ts +439 -0
- package/src/channels/bitbucket/state.ts +59 -0
- package/src/channels/bitbucket/types.ts +266 -0
- package/src/channels/deployments/deployments-channel.ts +25 -43
- package/src/channels/deployments/types.ts +12 -6
- package/src/channels/github/binding.ts +8 -1
- package/src/channels/github/cursor-account.ts +17 -6
- package/src/channels/github/events.ts +1 -1
- package/src/channels/github/forward.ts +3 -2
- package/src/channels/github/github-channel.ts +74 -221
- package/src/channels/github/replay.ts +1 -1
- package/src/channels/github/state.ts +6 -0
- package/src/channels/github/types.ts +2 -5
- package/src/channels/gitlab/api.ts +228 -0
- package/src/channels/gitlab/binding.ts +233 -0
- package/src/channels/gitlab/checks.ts +243 -0
- package/src/channels/gitlab/cursor-account.ts +114 -0
- package/src/channels/gitlab/defaults.ts +93 -0
- package/src/channels/gitlab/events.ts +44 -0
- package/src/channels/gitlab/gitlab-channel.ts +176 -0
- package/src/channels/gitlab/gitlab-webhook.ts +555 -0
- package/src/channels/gitlab/index.ts +43 -0
- package/src/channels/gitlab/replay.ts +347 -0
- package/src/channels/gitlab/state.ts +55 -0
- package/src/channels/gitlab/types.ts +263 -0
- package/src/channels/origin/api.ts +4 -2
- package/src/channels/origin/origin-channel.ts +19 -94
- package/src/channels/origin/types.ts +23 -2
- package/src/channels/slack/eval-directive.ts +1 -6
- package/src/channels/slack/init.ts +6 -3
- package/src/channels/slack/setup.ts +7 -6
- package/src/channels/slack/types.ts +1 -1
- package/src/channels.ts +0 -1
- package/src/client.ts +1 -1
- package/src/connections.ts +1 -1
- package/src/evals.ts +1 -1
- package/src/extensions.ts +47 -5
- package/src/files.ts +21 -3
- package/src/index.ts +20 -28
- package/src/internal/ab-snapshot.ts +0 -3
- package/src/internal/advertise-tools.ts +1 -4
- package/src/internal/authored-alias-hooks.ts +4 -21
- package/src/internal/authored-loaders.ts +6 -23
- package/src/internal/channel-dispatch.ts +160 -0
- package/src/internal/chat-client.ts +1 -1
- package/src/internal/cli-ax.ts +5 -5
- package/src/internal/cli-bitbucket.ts +477 -0
- package/src/internal/cli-convert-automation.ts +7 -7
- package/src/internal/cli-cursor.ts +2 -5
- package/src/internal/cli-deploy.ts +2 -8
- package/src/internal/cli-github.ts +3 -3
- package/src/internal/cli-gitlab.ts +485 -0
- package/src/internal/cli-mcp.ts +2 -7
- package/src/internal/cli-slack.ts +2 -5
- package/src/internal/continuation-channel.ts +82 -1
- package/src/internal/continuation-identity.ts +229 -11
- package/src/internal/conversation-mirror.ts +4 -3
- package/src/internal/convert-automation/types.ts +1 -1
- package/src/internal/cursor/account-mcp.ts +1 -4
- package/src/internal/cursor/backend-client.ts +9 -10
- package/src/internal/cursor/bound-secret-file.ts +108 -0
- package/src/internal/cursor/credentials.ts +75 -10
- package/src/internal/cursor/hosted-bound-secrets.ts +75 -0
- package/src/internal/cursor/hosted-deployment-secrets.ts +42 -11
- package/src/internal/cursor/hosted-store-secrets.ts +1 -1
- package/src/internal/cursor-account-mcp-auth.ts +2 -5
- package/src/internal/cursor-deployment-relay.ts +11 -7
- package/src/internal/deploy-manifest.ts +4 -2
- package/src/internal/discovery/ab.ts +29 -9
- package/src/internal/discovery/agent.ts +94 -135
- package/src/internal/discovery/channels.ts +15 -13
- package/src/internal/discovery/connections.ts +55 -50
- package/src/internal/discovery/cursor-plugin.ts +1 -4
- package/src/internal/discovery/extension-overlay.ts +274 -0
- package/src/internal/discovery/extensions.ts +330 -105
- package/src/internal/discovery/hooks.ts +20 -2
- package/src/internal/discovery/info.ts +7 -0
- package/src/internal/discovery/modules.ts +8 -2
- package/src/internal/discovery/names.ts +2 -1
- package/src/internal/discovery/project.ts +80 -9
- package/src/internal/discovery/runtime.ts +1 -2
- package/src/internal/discovery/schedules.ts +28 -2
- package/src/internal/discovery/services.ts +37 -1
- package/src/internal/discovery/skills.ts +13 -3
- package/src/internal/discovery/subagent.ts +107 -0
- package/src/internal/discovery.ts +0 -2
- package/src/internal/distribution.ts +35 -83
- package/src/internal/docs-site.ts +1 -1
- package/src/internal/eval-run-store.ts +0 -7
- package/src/internal/eval-runner.ts +0 -1
- package/src/internal/framework-file-storage.ts +8 -8
- package/src/internal/github-fanout.ts +1 -1
- package/src/internal/guest-network.ts +383 -0
- package/src/internal/host-otel.ts +8 -4
- package/src/internal/hosted-admission-adapter.ts +8 -8
- package/src/internal/hosted-delivery-protocol.ts +26 -1
- package/src/internal/hosted-delivery.ts +376 -192
- package/src/internal/http-channel.ts +1 -4
- package/src/internal/init-interview.ts +1 -4
- package/src/internal/init-project.ts +2 -2
- package/src/internal/inline-stdio-define-stub.ts +1 -1
- package/src/internal/is-record.ts +3 -0
- package/src/internal/json-schema.ts +19 -10
- package/src/internal/local-control-plane.ts +5 -24
- package/src/internal/mcp-endpoint.ts +1 -4
- package/src/internal/otel/collector.ts +94 -159
- package/src/internal/otel/config.ts +10 -14
- package/src/internal/otel/http.ts +14 -1
- package/src/internal/otel/mcp.ts +3 -16
- package/src/internal/otel/runtime.ts +240 -25
- package/src/internal/package-inline-stdio.ts +3 -12
- package/src/internal/playground/toolchain.ts +1 -1
- package/src/internal/request-error-status.ts +72 -0
- package/src/internal/resolve-prod-target.ts +3 -8
- package/src/internal/result-output.ts +34 -0
- package/src/internal/run-client.ts +2 -2
- package/src/internal/runtime-dispatch-runner.ts +3 -21
- package/src/internal/sdk-runner.ts +3 -7
- package/src/internal/server.ts +153 -44
- package/src/internal/session-cost.ts +3 -3
- package/src/internal/session-engine.ts +182 -74
- package/src/internal/session-run-log.ts +1 -4
- package/src/internal/skills-sync.ts +13 -63
- package/src/internal/sleep.ts +3 -0
- package/src/internal/store-mirror.ts +86 -0
- package/src/internal/tenant-slack/relay.ts +179 -0
- package/src/internal/testing/scm-continuation-fixtures.ts +286 -0
- package/src/internal/workspace-sync.ts +55 -0
- package/src/internal/workspace.ts +14 -0
- package/src/otel.ts +143 -3
- package/src/result.ts +36 -0
- package/src/storage-backends/cursor-hosted.ts +7 -4
- package/src/storage-protocol.ts +1 -4
- package/src/types.ts +105 -34
- package/templates/triage/agent/lib/types.ts +0 -2
- package/dist/continuation.d.ts +0 -20
- package/dist/continuation.d.ts.map +0 -1
- package/dist/continuation.js +0 -18
- package/dist/docs/assets/chunks/@localSearchIndexroot.FAqdWIXo.js +0 -1
- package/dist/docs/assets/concepts.md.rPzW2HZN.js +0 -1
- package/dist/docs/assets/guides_cloud-agents.md.DumwWXba.js +0 -9
- package/dist/docs/assets/guides_cloud-runtime.md.BRirbRAN.js +0 -9
- package/dist/docs/assets/guides_cloud-runtime.md.BRirbRAN.lean.js +0 -1
- package/dist/docs/assets/reference_extensions.md.9WQMZe1l.lean.js +0 -1
- package/dist/docs/assets/reference_hooks.md.a8BJxMR5.js +0 -14
- package/dist/docs/assets/skills_framework-map.md.DDa4MeEW.js +0 -1
- package/dist/docs/guides/cloud-runtime.html +0 -35
- package/dist/docs/guides/cloud-runtime.md +0 -117
- package/dist/internal/convert-automation/index.d.ts +0 -3
- package/dist/internal/convert-automation/index.d.ts.map +0 -1
- package/dist/internal/convert-automation/index.js +0 -1
- package/dist/internal/grokbot/runner.d.ts +0 -64
- package/dist/internal/grokbot/runner.d.ts.map +0 -1
- package/dist/internal/grokbot/runner.js +0 -282
- package/dist/internal/managed-delivery.d.ts +0 -80
- package/dist/internal/managed-delivery.d.ts.map +0 -1
- package/dist/internal/managed-delivery.js +0 -86
- package/dist/internal/review-comments.d.ts +0 -370
- package/dist/internal/review-comments.d.ts.map +0 -1
- package/dist/internal/review-comments.js +0 -921
- package/dist/internal/shallow-clone.d.ts +0 -176
- package/dist/internal/shallow-clone.d.ts.map +0 -1
- package/dist/internal/shallow-clone.js +0 -419
- package/dist/playground/assets/index-B6C9S9nG.css +0 -1
- package/dist/playground/assets/index-ZqNoT8Ie.js +0 -69
- package/docs/guides/cloud-runtime.md +0 -122
- package/src/continuation.ts +0 -49
- package/src/internal/convert-automation/index.ts +0 -10
- package/src/internal/grokbot/runner.ts +0 -370
- package/src/internal/managed-delivery.ts +0 -125
- package/src/internal/review-comments.ts +0 -1363
- package/src/internal/shallow-clone.ts +0 -609
- /package/dist/docs/assets/{evals.md.CPzDAwoH.lean.js → evals.md.CbMoebP1.lean.js} +0 -0
- /package/dist/docs/assets/{guides_cloud-agents.md.DumwWXba.lean.js → guides_cloud-agents.md.DS8RIjwx.lean.js} +0 -0
- /package/dist/docs/assets/{guides_github.md.BtPr9GaP.lean.js → guides_github.md.DKYiT9do.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.Kiht7ell.lean.js → index.md.D75GWhPl.lean.js} +0 -0
- /package/dist/docs/assets/{reference_agent-config.md.BHeYMgme.lean.js → reference_agent-config.md.CHNpiyp4.lean.js} +0 -0
- /package/dist/docs/assets/{reference_cli.md.DfoeyvL0.lean.js → reference_cli.md.CJNc_xYK.lean.js} +0 -0
- /package/dist/docs/assets/{reference_playground.md.DnX5nL-B.lean.js → reference_playground.md.4myJPxrf.lean.js} +0 -0
- /package/dist/docs/assets/{reference_project-layout.md.CA6QP6Lw.lean.js → reference_project-layout.md.DuBu9a96.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.B1up2r9W.lean.js → reference_tools.md.aGrn99hP.lean.js} +0 -0
- /package/dist/docs/assets/{skills_framework-map.md.DDa4MeEW.lean.js → skills_framework-map.md.haibFyoB.lean.js} +0 -0
|
@@ -1,1363 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import type { HostGitHubClient } from "../types.js";
|
|
3
|
-
import { parsePrUrl } from "./scm/pr-url.js";
|
|
4
|
-
|
|
5
|
-
export type ReviewCommentPublisher = "local_gh" | "host_github" | "host_origin";
|
|
6
|
-
|
|
7
|
-
export type ReviewCommentPostResult = {
|
|
8
|
-
posted: boolean;
|
|
9
|
-
publisher: ReviewCommentPublisher;
|
|
10
|
-
mode: "review_with_inline_comments" | "review_body_only" | "skipped";
|
|
11
|
-
inlineCommentCount: number;
|
|
12
|
-
detail: string;
|
|
13
|
-
/**
|
|
14
|
-
* Ids of the inline comments this post created, when the publisher could
|
|
15
|
-
* read them back. Callers that later re-read a PR's comments use these as
|
|
16
|
-
* the provenance allowlist: a comment id we did not create is not ours,
|
|
17
|
-
* no matter what its body looks like. Empty when the publisher cannot
|
|
18
|
-
* resolve them (it never guesses).
|
|
19
|
-
*/
|
|
20
|
-
postedComments?: PostedReviewComment[];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/** One inline comment this agent created, as the SCM assigned it. */
|
|
24
|
-
export type PostedReviewComment = {
|
|
25
|
-
id: number;
|
|
26
|
-
file: string;
|
|
27
|
-
line: number;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type ReviewInlineComment = {
|
|
31
|
-
file: string;
|
|
32
|
-
line: number;
|
|
33
|
-
start_line?: number;
|
|
34
|
-
body: string;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type ReviewCommentPostArgs = {
|
|
38
|
-
prUrl: string;
|
|
39
|
-
reviewedHeadSha: string;
|
|
40
|
-
message: string;
|
|
41
|
-
comments: ReviewInlineComment[];
|
|
42
|
-
cleanupPrevious?: boolean;
|
|
43
|
-
signal?: AbortSignal;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export type ReviewCommentsRequest = {
|
|
47
|
-
provider: "github";
|
|
48
|
-
repo: string;
|
|
49
|
-
prNumber: number;
|
|
50
|
-
headSha: string;
|
|
51
|
-
message: string;
|
|
52
|
-
comments: ReviewInlineComment[];
|
|
53
|
-
cleanupPrevious: boolean;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export type ReviewCommentPublisherImplementation = {
|
|
57
|
-
postReview: (args: ReviewCommentPostArgs) => Promise<ReviewCommentPostResult>;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* SCMs this module can address. Same growth seam as `shallow-clone.ts`'s
|
|
62
|
-
* {@link import("./shallow-clone.js").ScmProvider}: adding a forge means a
|
|
63
|
-
* resolver case plus a publisher (and, when the public API supports it, a
|
|
64
|
-
* reader / thread resolver). Callers above this line stay provider-neutral.
|
|
65
|
-
*/
|
|
66
|
-
export type ScmReviewProvider = "github" | "origin";
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Origin public REST client used to post PR reviews (`ctx.origin` /
|
|
70
|
-
* {@link import("../channels/origin/api.js").createOriginClient}). Narrower
|
|
71
|
-
* than the full Origin handle so tests and CLI paths can inject `request()`.
|
|
72
|
-
*/
|
|
73
|
-
export type HostOriginReviewClient = {
|
|
74
|
-
request(input: {
|
|
75
|
-
method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
|
|
76
|
-
path: string;
|
|
77
|
-
body?: unknown;
|
|
78
|
-
}): Promise<unknown>;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/** Which SCM's PR to read review comments from. */
|
|
82
|
-
export type ScmReviewTarget = {
|
|
83
|
-
provider: ScmReviewProvider;
|
|
84
|
-
/** `owner/name`. */
|
|
85
|
-
repo: string;
|
|
86
|
-
prNumber: number;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/** Optional structured logger for the read/resolve seams (`[asr]`-style). */
|
|
90
|
-
export type ReviewCommentLog = (message: string) => void;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* One inline review-thread comment from a PR-wide listing, provider-neutral.
|
|
94
|
-
*
|
|
95
|
-
* This is the Bugbot listing shape (`minimizePreviousBugBotComments` /
|
|
96
|
-
* `getBugBotComments` normalize each provider's `getPRComments` output the
|
|
97
|
-
* same way): a re-review lists everything on the PR and filters on its own
|
|
98
|
-
* marker, rather than fetching ids it recorded elsewhere.
|
|
99
|
-
*/
|
|
100
|
-
export type ListedReviewComment = {
|
|
101
|
-
/**
|
|
102
|
-
* Provider-native comment id, stringified for provider neutrality
|
|
103
|
-
* (GitHub REST database id in decimal; Origin `cmt_…`).
|
|
104
|
-
*/
|
|
105
|
-
commentId: string;
|
|
106
|
-
/** GitHub REST numeric comment id, when the provider exposes one. */
|
|
107
|
-
numericCommentId?: number;
|
|
108
|
-
/**
|
|
109
|
-
* Id of the thread this comment opens, in the form the provider's thread
|
|
110
|
-
* resolver accepts (GitHub GraphQL thread node id; Origin `cth_…`).
|
|
111
|
-
*/
|
|
112
|
-
threadId: string;
|
|
113
|
-
/** Whether the owning thread is already resolved. */
|
|
114
|
-
resolved: boolean;
|
|
115
|
-
file: string;
|
|
116
|
-
startLine: number;
|
|
117
|
-
endLine: number;
|
|
118
|
-
body: string;
|
|
119
|
-
/**
|
|
120
|
-
* Head SHA the comment was filed against (GitHub `originalCommit`; Origin
|
|
121
|
-
* the thread version's head SHA), when the provider exposes it. This is
|
|
122
|
-
* the commit a follow-up judge diffs forward from.
|
|
123
|
-
*/
|
|
124
|
-
commitSha?: string;
|
|
125
|
-
authorLogin?: string;
|
|
126
|
-
/**
|
|
127
|
-
* Whether the SCM attributes the comment to a machine identity — a
|
|
128
|
-
* GitHub App bot (GraphQL author `__typename: "Bot"`) or an Origin
|
|
129
|
-
* app/service-account actor. Human accounts are `false`; `undefined`
|
|
130
|
-
* when the provider did not return an author (e.g. deleted account).
|
|
131
|
-
* Callers deciding whether a comment is their own must require `true`
|
|
132
|
-
* AND pin the specific identity (login / app id): body content is
|
|
133
|
-
* forgeable by any PR participant, and other bots/apps also comment.
|
|
134
|
-
*/
|
|
135
|
-
authorIsBot?: boolean;
|
|
136
|
-
/**
|
|
137
|
-
* Origin app actor id (`app_…`) when the comment was authored by an app
|
|
138
|
-
* installation. Callers compare it against their own configured app id
|
|
139
|
-
* to decide the comment is theirs, not merely some app's.
|
|
140
|
-
*/
|
|
141
|
-
authorAppId?: string;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
export type ListReviewCommentsArgs = {
|
|
145
|
-
target: ScmReviewTarget;
|
|
146
|
-
signal?: AbortSignal;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Read side of the review-comment seam, mirroring
|
|
151
|
-
* {@link ReviewCommentPublisherImplementation}. Bugbot's equivalent is
|
|
152
|
-
* `IGitProvider.getPRComments`: list the PR's inline review threads (their
|
|
153
|
-
* opening comments) so a re-review can find its own previous comments.
|
|
154
|
-
*/
|
|
155
|
-
export type ReviewCommentListerImplementation = {
|
|
156
|
-
listReviewComments: (
|
|
157
|
-
args: ListReviewCommentsArgs
|
|
158
|
-
) => Promise<ListedReviewComment[]>;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
export type ResolveReviewThreadsArgs = {
|
|
162
|
-
target: ScmReviewTarget;
|
|
163
|
-
/** Provider-native thread ids (from {@link ListedReviewComment.threadId}). */
|
|
164
|
-
threadIds: readonly string[];
|
|
165
|
-
signal?: AbortSignal;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Outcome of resolving a set of review threads. Callers treat this as
|
|
170
|
-
* best-effort actuation: a failed resolve must not fail the review that
|
|
171
|
-
* already posted.
|
|
172
|
-
*/
|
|
173
|
-
export type ResolveReviewThreadsResult = {
|
|
174
|
-
requested: number;
|
|
175
|
-
resolved: number;
|
|
176
|
-
failed: number;
|
|
177
|
-
detail: string;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Write side of thread resolution, the counterpart of
|
|
182
|
-
* {@link ReviewCommentListerImplementation}. Bugbot's equivalent is
|
|
183
|
-
* `IGitProvider.resolveReviewThreadForComment` / `resolveThreads`.
|
|
184
|
-
*
|
|
185
|
-
* GitHub only exposes resolve via GraphQL (`resolveReviewThread`). REST
|
|
186
|
-
* has no equivalent. The mutation runs as whatever identity minted the
|
|
187
|
-
* bound GitHub client — for `githubChannel({ cursorAccount })` that is
|
|
188
|
-
* the Cursor installation, the same identity that posted the comments.
|
|
189
|
-
* Origin exposes resolve on the public REST API
|
|
190
|
-
* (`PATCH …/pulls/threads/{thread_id}`).
|
|
191
|
-
*/
|
|
192
|
-
export type ReviewThreadResolverImplementation = {
|
|
193
|
-
resolveReviewThreads: (
|
|
194
|
-
args: ResolveReviewThreadsArgs
|
|
195
|
-
) => Promise<ResolveReviewThreadsResult>;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/** Parse a PR URL into the SCM target to read or post comments on. */
|
|
199
|
-
export function resolveScmReviewTarget(
|
|
200
|
-
prUrl: string
|
|
201
|
-
): ScmReviewTarget | undefined {
|
|
202
|
-
const parsed = parsePrUrl(prUrl);
|
|
203
|
-
if (parsed === undefined) {
|
|
204
|
-
return undefined;
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
provider: parsed.provider,
|
|
208
|
-
repo: `${parsed.owner}/${parsed.repo}`,
|
|
209
|
-
prNumber: parsed.number,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Lister for a resolved target, mirroring Bugbot's `createPrGitProvider`
|
|
215
|
-
* dispatch. Returns undefined when the host cannot serve that provider (no
|
|
216
|
-
* GitHub/Origin client bound), so callers degrade instead of throwing.
|
|
217
|
-
*/
|
|
218
|
-
export function createReviewCommentLister(args: {
|
|
219
|
-
target: ScmReviewTarget;
|
|
220
|
-
github?: HostGitHubClient;
|
|
221
|
-
origin?: HostOriginReviewClient;
|
|
222
|
-
log?: ReviewCommentLog;
|
|
223
|
-
}): ReviewCommentListerImplementation | undefined {
|
|
224
|
-
switch (args.target.provider) {
|
|
225
|
-
case "github":
|
|
226
|
-
return args.github === undefined
|
|
227
|
-
? undefined
|
|
228
|
-
: createHostGithubReviewCommentLister(args.github, args.log);
|
|
229
|
-
case "origin":
|
|
230
|
-
return args.origin === undefined
|
|
231
|
-
? undefined
|
|
232
|
-
: createHostOriginReviewCommentLister(args.origin, args.log);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Resolver for a resolved target. Returns undefined when the host cannot
|
|
238
|
-
* serve that provider, so callers degrade instead of throwing.
|
|
239
|
-
*/
|
|
240
|
-
export function createReviewThreadResolver(args: {
|
|
241
|
-
target: ScmReviewTarget;
|
|
242
|
-
github?: HostGitHubClient;
|
|
243
|
-
origin?: HostOriginReviewClient;
|
|
244
|
-
log?: ReviewCommentLog;
|
|
245
|
-
}): ReviewThreadResolverImplementation | undefined {
|
|
246
|
-
switch (args.target.provider) {
|
|
247
|
-
case "github":
|
|
248
|
-
return args.github === undefined
|
|
249
|
-
? undefined
|
|
250
|
-
: createHostGithubReviewThreadResolver(args.github, args.log);
|
|
251
|
-
case "origin":
|
|
252
|
-
return args.origin === undefined
|
|
253
|
-
? undefined
|
|
254
|
-
: createHostOriginReviewThreadResolver(args.origin, args.log);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Resolve `threadIds` on `prUrl`'s PR.
|
|
260
|
-
*
|
|
261
|
-
* No-ops (never throws) when the PR is on an SCM this host cannot write,
|
|
262
|
-
* no client is bound, or `threadIds` is empty. Same degrade-don't-fail
|
|
263
|
-
* posture as {@link createReviewCommentLister}.
|
|
264
|
-
*/
|
|
265
|
-
export async function resolveReviewThreads(args: {
|
|
266
|
-
prUrl: string;
|
|
267
|
-
threadIds: readonly string[];
|
|
268
|
-
github?: HostGitHubClient;
|
|
269
|
-
origin?: HostOriginReviewClient;
|
|
270
|
-
resolver?: ReviewThreadResolverImplementation;
|
|
271
|
-
signal?: AbortSignal;
|
|
272
|
-
log?: ReviewCommentLog;
|
|
273
|
-
}): Promise<ResolveReviewThreadsResult> {
|
|
274
|
-
if (args.threadIds.length === 0) {
|
|
275
|
-
return skippedResolve("no threads to resolve");
|
|
276
|
-
}
|
|
277
|
-
const target = resolveScmReviewTarget(args.prUrl);
|
|
278
|
-
if (target === undefined) {
|
|
279
|
-
return skippedResolve("no SCM resolver for this PR URL");
|
|
280
|
-
}
|
|
281
|
-
const resolver =
|
|
282
|
-
args.resolver ??
|
|
283
|
-
createReviewThreadResolver({
|
|
284
|
-
target,
|
|
285
|
-
github: args.github,
|
|
286
|
-
origin: args.origin,
|
|
287
|
-
log: args.log,
|
|
288
|
-
});
|
|
289
|
-
if (resolver === undefined) {
|
|
290
|
-
return skippedResolve(
|
|
291
|
-
`no ${target.provider} client bound to resolve threads`
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
return resolver.resolveReviewThreads({
|
|
295
|
-
target,
|
|
296
|
-
threadIds: args.threadIds,
|
|
297
|
-
signal: args.signal,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/** Cap on pages pulled per PR; GitHub serves 100 comments per page. */
|
|
302
|
-
const REVIEW_COMMENTS_MAX_PAGES = 5;
|
|
303
|
-
const REVIEW_COMMENTS_PAGE_SIZE = 100;
|
|
304
|
-
/** Cap on review-thread pages when listing a PR's inline threads. */
|
|
305
|
-
const REVIEW_THREADS_MAX_PAGES = 20;
|
|
306
|
-
const REVIEW_THREADS_PAGE_SIZE = 100;
|
|
307
|
-
/** Cap on Origin comment pages pulled per PR (100 comments per page). */
|
|
308
|
-
const ORIGIN_COMMENTS_MAX_PAGES = 20;
|
|
309
|
-
const ORIGIN_COMMENTS_PAGE_SIZE = 100;
|
|
310
|
-
|
|
311
|
-
type GithubThreadCommentNode = {
|
|
312
|
-
databaseId?: number | null;
|
|
313
|
-
body?: string | null;
|
|
314
|
-
author?: { login?: string | null; __typename?: string | null } | null;
|
|
315
|
-
originalCommit?: { oid?: string | null } | null;
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
type GithubReviewThreadNode = {
|
|
319
|
-
id?: string;
|
|
320
|
-
isResolved?: boolean;
|
|
321
|
-
path?: string | null;
|
|
322
|
-
line?: number | null;
|
|
323
|
-
originalLine?: number | null;
|
|
324
|
-
startLine?: number | null;
|
|
325
|
-
originalStartLine?: number | null;
|
|
326
|
-
comments?: {
|
|
327
|
-
nodes?: Array<GithubThreadCommentNode | null>;
|
|
328
|
-
};
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
type GithubReviewThreadsQuery = {
|
|
332
|
-
repository?: {
|
|
333
|
-
pullRequest?: {
|
|
334
|
-
reviewThreads?: {
|
|
335
|
-
nodes?: Array<GithubReviewThreadNode | null>;
|
|
336
|
-
pageInfo?: { hasNextPage?: boolean; endCursor?: string | null };
|
|
337
|
-
};
|
|
338
|
-
} | null;
|
|
339
|
-
} | null;
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
type GithubResolveThreadMutation = {
|
|
343
|
-
resolveReviewThread?: {
|
|
344
|
-
thread?: { isResolved?: boolean };
|
|
345
|
-
};
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Repo-scope header for GraphQL calls through the Cursor github-proxy.
|
|
350
|
-
*
|
|
351
|
-
* The proxy authorizes each call from `x-cursor-github-repos`, which the
|
|
352
|
-
* channel Octokit derives from REST URLs (`/repos/{owner}/{repo}/…`).
|
|
353
|
-
* GraphQL posts to `/graphql` — no repo in the URL — so without this
|
|
354
|
-
* explicit header every GraphQL call is rejected in proxy mode. Harmless
|
|
355
|
-
* against api.github.com, which ignores unknown headers.
|
|
356
|
-
*/
|
|
357
|
-
function githubGraphqlScopeHeaders(repo: string): Record<string, string> {
|
|
358
|
-
return { "x-cursor-github-repos": repo.toLowerCase() };
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* GitHub review thread → {@link ListedReviewComment} (its opening comment).
|
|
363
|
-
*
|
|
364
|
-
* Prefers the `original*` line fields — the position as of the review the
|
|
365
|
-
* thread belongs to. `line` drifts as the PR gains commits, so a caller
|
|
366
|
-
* correlating a comment with the review that produced it needs the original
|
|
367
|
-
* position, the same fields Bugbot carries on `PullRequestCommentOutput`.
|
|
368
|
-
*/
|
|
369
|
-
export function listedCommentFromGithubThread(
|
|
370
|
-
thread: GithubReviewThreadNode
|
|
371
|
-
): ListedReviewComment | undefined {
|
|
372
|
-
const opener = thread.comments?.nodes?.[0];
|
|
373
|
-
if (
|
|
374
|
-
thread.id === undefined ||
|
|
375
|
-
thread.id === "" ||
|
|
376
|
-
opener === null ||
|
|
377
|
-
opener === undefined ||
|
|
378
|
-
opener.databaseId === undefined ||
|
|
379
|
-
opener.databaseId === null ||
|
|
380
|
-
typeof opener.body !== "string" ||
|
|
381
|
-
typeof thread.path !== "string" ||
|
|
382
|
-
thread.path === ""
|
|
383
|
-
) {
|
|
384
|
-
return undefined;
|
|
385
|
-
}
|
|
386
|
-
// Take both lines from the same position set. Mixing them (original end
|
|
387
|
-
// line with the drifted current start line) would describe a range that
|
|
388
|
-
// never existed in either revision.
|
|
389
|
-
const original = thread.originalLine ?? undefined;
|
|
390
|
-
const endLine = original ?? thread.line ?? undefined;
|
|
391
|
-
if (endLine === undefined) {
|
|
392
|
-
return undefined;
|
|
393
|
-
}
|
|
394
|
-
const startLine =
|
|
395
|
-
(original === undefined ? thread.startLine : thread.originalStartLine) ??
|
|
396
|
-
endLine;
|
|
397
|
-
const commitSha = opener.originalCommit?.oid ?? undefined;
|
|
398
|
-
const authorLogin = opener.author?.login ?? undefined;
|
|
399
|
-
const authorType = opener.author?.__typename ?? undefined;
|
|
400
|
-
return {
|
|
401
|
-
commentId: String(opener.databaseId),
|
|
402
|
-
numericCommentId: opener.databaseId,
|
|
403
|
-
threadId: thread.id,
|
|
404
|
-
resolved: thread.isResolved === true,
|
|
405
|
-
file: thread.path,
|
|
406
|
-
startLine,
|
|
407
|
-
endLine,
|
|
408
|
-
body: opener.body,
|
|
409
|
-
...(commitSha === undefined ? {} : { commitSha }),
|
|
410
|
-
...(authorLogin === undefined ? {} : { authorLogin }),
|
|
411
|
-
...(authorType === undefined ? {} : { authorIsBot: authorType === "Bot" }),
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Lists the PR's inline review threads (their opening comments) through the
|
|
417
|
-
* host GitHub client (Cursor account lease, App installation token, or
|
|
418
|
-
* PAT). One paged GraphQL query yields, per thread: the resolvable thread
|
|
419
|
-
* node id, its resolution state, the opener's body/position, and the commit
|
|
420
|
-
* it was filed against — everything a Bugbot-style re-review needs.
|
|
421
|
-
*/
|
|
422
|
-
export function createHostGithubReviewCommentLister(
|
|
423
|
-
github: HostGitHubClient,
|
|
424
|
-
log?: ReviewCommentLog
|
|
425
|
-
): ReviewCommentListerImplementation {
|
|
426
|
-
return {
|
|
427
|
-
async listReviewComments({ target, signal }) {
|
|
428
|
-
const [owner, repo] = target.repo.split("/", 2);
|
|
429
|
-
if (owner === undefined || repo === undefined || repo === "") {
|
|
430
|
-
log?.(`github list: unparseable owner/repo "${target.repo}"`);
|
|
431
|
-
return [];
|
|
432
|
-
}
|
|
433
|
-
const octokit = await github.getOctokit();
|
|
434
|
-
const comments: ListedReviewComment[] = [];
|
|
435
|
-
let threadCount = 0;
|
|
436
|
-
let cursor: string | null = null;
|
|
437
|
-
for (let page = 0; page < REVIEW_THREADS_MAX_PAGES; page++) {
|
|
438
|
-
signal?.throwIfAborted();
|
|
439
|
-
const response = (await octokit.graphql(
|
|
440
|
-
REVIEW_THREAD_COMMENTS_FOR_PR_QUERY,
|
|
441
|
-
{
|
|
442
|
-
owner,
|
|
443
|
-
repo,
|
|
444
|
-
number: target.prNumber,
|
|
445
|
-
cursor,
|
|
446
|
-
headers: githubGraphqlScopeHeaders(target.repo),
|
|
447
|
-
}
|
|
448
|
-
)) as GithubReviewThreadsQuery;
|
|
449
|
-
const reviewThreads = response.repository?.pullRequest?.reviewThreads;
|
|
450
|
-
for (const thread of reviewThreads?.nodes ?? []) {
|
|
451
|
-
if (thread === null || thread === undefined) {
|
|
452
|
-
continue;
|
|
453
|
-
}
|
|
454
|
-
threadCount += 1;
|
|
455
|
-
const listed = listedCommentFromGithubThread(thread);
|
|
456
|
-
if (listed !== undefined) {
|
|
457
|
-
comments.push(listed);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
const pageInfo = reviewThreads?.pageInfo;
|
|
461
|
-
if (pageInfo?.hasNextPage !== true || !pageInfo.endCursor) {
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
cursor = pageInfo.endCursor;
|
|
465
|
-
}
|
|
466
|
-
log?.(
|
|
467
|
-
`github list: ${target.repo}#${target.prNumber} has ${threadCount} review thread(s), ${comments.length} with a normalizable opening comment`
|
|
468
|
-
);
|
|
469
|
-
return comments;
|
|
470
|
-
},
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Resolves inline review threads by GraphQL thread node id through the host
|
|
476
|
-
* GitHub client (Cursor account lease, App installation token, or PAT) —
|
|
477
|
-
* the id the lister already returned, so no comment→thread mapping pass is
|
|
478
|
-
* needed. Same mutation as Bugbot's `resolveReviewThreadForComment`.
|
|
479
|
-
*/
|
|
480
|
-
export function createHostGithubReviewThreadResolver(
|
|
481
|
-
github: HostGitHubClient,
|
|
482
|
-
log?: ReviewCommentLog
|
|
483
|
-
): ReviewThreadResolverImplementation {
|
|
484
|
-
return {
|
|
485
|
-
async resolveReviewThreads({ target, threadIds, signal }) {
|
|
486
|
-
const uniqueIds = [...new Set(threadIds.filter((id) => id !== ""))];
|
|
487
|
-
if (uniqueIds.length === 0) {
|
|
488
|
-
return skippedResolve("no threads to resolve");
|
|
489
|
-
}
|
|
490
|
-
let resolved = 0;
|
|
491
|
-
let failed = 0;
|
|
492
|
-
try {
|
|
493
|
-
const octokit = await github.getOctokit();
|
|
494
|
-
for (const threadId of uniqueIds) {
|
|
495
|
-
signal?.throwIfAborted();
|
|
496
|
-
try {
|
|
497
|
-
const mutation = (await octokit.graphql(
|
|
498
|
-
RESOLVE_REVIEW_THREAD_MUTATION,
|
|
499
|
-
{
|
|
500
|
-
threadId,
|
|
501
|
-
headers: githubGraphqlScopeHeaders(target.repo),
|
|
502
|
-
}
|
|
503
|
-
)) as GithubResolveThreadMutation;
|
|
504
|
-
if (mutation.resolveReviewThread?.thread?.isResolved === true) {
|
|
505
|
-
resolved += 1;
|
|
506
|
-
log?.(`github resolve: thread ${threadId} resolved`);
|
|
507
|
-
} else {
|
|
508
|
-
failed += 1;
|
|
509
|
-
log?.(
|
|
510
|
-
`github resolve: thread ${threadId} mutation did not mark the thread resolved`
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
} catch (error) {
|
|
514
|
-
if (signal?.aborted) {
|
|
515
|
-
throw error;
|
|
516
|
-
}
|
|
517
|
-
failed += 1;
|
|
518
|
-
log?.(
|
|
519
|
-
`github resolve: thread ${threadId} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
requested: uniqueIds.length,
|
|
525
|
-
resolved,
|
|
526
|
-
failed,
|
|
527
|
-
detail: `resolved ${resolved} of ${uniqueIds.length} thread(s) on ${target.repo}#${target.prNumber} via host GitHub (${failed} failed)`,
|
|
528
|
-
};
|
|
529
|
-
} catch (error) {
|
|
530
|
-
if (signal?.aborted) {
|
|
531
|
-
throw error;
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
requested: uniqueIds.length,
|
|
535
|
-
resolved,
|
|
536
|
-
failed: uniqueIds.length - resolved,
|
|
537
|
-
detail: `host GitHub thread resolve failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
},
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
const REVIEW_THREAD_COMMENTS_FOR_PR_QUERY = `
|
|
545
|
-
query ReviewThreadCommentsForPr(
|
|
546
|
-
$owner: String!
|
|
547
|
-
$repo: String!
|
|
548
|
-
$number: Int!
|
|
549
|
-
$cursor: String
|
|
550
|
-
) {
|
|
551
|
-
repository(owner: $owner, name: $repo) {
|
|
552
|
-
pullRequest(number: $number) {
|
|
553
|
-
reviewThreads(first: ${REVIEW_THREADS_PAGE_SIZE}, after: $cursor) {
|
|
554
|
-
nodes {
|
|
555
|
-
id
|
|
556
|
-
isResolved
|
|
557
|
-
path
|
|
558
|
-
line
|
|
559
|
-
originalLine
|
|
560
|
-
startLine
|
|
561
|
-
originalStartLine
|
|
562
|
-
comments(first: 1) {
|
|
563
|
-
nodes {
|
|
564
|
-
databaseId
|
|
565
|
-
body
|
|
566
|
-
author { login __typename }
|
|
567
|
-
originalCommit { oid }
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
pageInfo { hasNextPage endCursor }
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
`;
|
|
577
|
-
|
|
578
|
-
const RESOLVE_REVIEW_THREAD_MUTATION = `
|
|
579
|
-
mutation ResolveReviewThread($threadId: ID!) {
|
|
580
|
-
resolveReviewThread(input: { threadId: $threadId }) {
|
|
581
|
-
thread { isResolved }
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
`;
|
|
585
|
-
|
|
586
|
-
const GITHUB_VIEWER_LOGIN_QUERY = `
|
|
587
|
-
query ViewerLogin {
|
|
588
|
-
viewer { login }
|
|
589
|
-
}
|
|
590
|
-
`;
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* The login the bound GitHub client itself acts as — the identity its posts
|
|
594
|
-
* appear under. Works for installation tokens too (GraphQL `viewer` returns
|
|
595
|
-
* the app's bot, e.g. `cursor[bot]`), so a reader can trust comments whose
|
|
596
|
-
* author is provably the same identity it posts with, across every
|
|
597
|
-
* environment's app (prod/staging/cloud-dev/custom) without hardcoding.
|
|
598
|
-
* `repo` scopes the call through the Cursor github-proxy (the minted token
|
|
599
|
-
* — and so the viewer — is per-repo there). Best-effort: `undefined` when
|
|
600
|
-
* the lookup fails.
|
|
601
|
-
*/
|
|
602
|
-
export async function fetchGithubSelfLogin(
|
|
603
|
-
github: HostGitHubClient,
|
|
604
|
-
args: { repo: string; signal?: AbortSignal; log?: ReviewCommentLog }
|
|
605
|
-
): Promise<string | undefined> {
|
|
606
|
-
try {
|
|
607
|
-
const octokit = await github.getOctokit();
|
|
608
|
-
args.signal?.throwIfAborted();
|
|
609
|
-
const response = (await octokit.graphql(GITHUB_VIEWER_LOGIN_QUERY, {
|
|
610
|
-
headers: githubGraphqlScopeHeaders(args.repo),
|
|
611
|
-
})) as {
|
|
612
|
-
viewer?: { login?: string | null } | null;
|
|
613
|
-
};
|
|
614
|
-
const login = response.viewer?.login;
|
|
615
|
-
return typeof login === "string" && login !== "" ? login : undefined;
|
|
616
|
-
} catch (error) {
|
|
617
|
-
if (args.signal?.aborted) {
|
|
618
|
-
throw error;
|
|
619
|
-
}
|
|
620
|
-
args.log?.(
|
|
621
|
-
`github viewer lookup failed: ${error instanceof Error ? error.message : String(error)}`
|
|
622
|
-
);
|
|
623
|
-
return undefined;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
type OriginThreadJson = {
|
|
628
|
-
id?: string;
|
|
629
|
-
path?: string;
|
|
630
|
-
startLine?: number;
|
|
631
|
-
endLine?: number;
|
|
632
|
-
resolvedAt?: string;
|
|
633
|
-
version?: { headSha?: string };
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* Public `OriginActor` proto3 JSON: a `user` / `app` / `serviceAccount`
|
|
638
|
-
* oneof (see `origin_actor.proto`).
|
|
639
|
-
*/
|
|
640
|
-
type OriginActorJson = {
|
|
641
|
-
user?: { id?: string; email?: string };
|
|
642
|
-
// Apps have no slug (they are id + display name); `slug` is being retired
|
|
643
|
-
// from the public actor and is never read here.
|
|
644
|
-
app?: { id?: string; displayName?: string };
|
|
645
|
-
serviceAccount?: { id?: string };
|
|
646
|
-
};
|
|
647
|
-
|
|
648
|
-
type OriginCommentJson = {
|
|
649
|
-
id?: string;
|
|
650
|
-
body?: string;
|
|
651
|
-
thread?: OriginThreadJson;
|
|
652
|
-
author?: OriginActorJson;
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
type OriginListCommentsJson = {
|
|
656
|
-
comments?: Array<OriginCommentJson | null>;
|
|
657
|
-
nextPageToken?: string;
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
/**
|
|
661
|
-
* Origin public comment → {@link ListedReviewComment}. Only line-anchored
|
|
662
|
-
* threads qualify (general-discussion and file-level threads carry no
|
|
663
|
-
* position a review finding can be pinned to).
|
|
664
|
-
*/
|
|
665
|
-
export function listedCommentFromOriginComment(
|
|
666
|
-
comment: OriginCommentJson
|
|
667
|
-
): ListedReviewComment | undefined {
|
|
668
|
-
const thread = comment.thread;
|
|
669
|
-
if (
|
|
670
|
-
typeof comment.id !== "string" ||
|
|
671
|
-
comment.id === "" ||
|
|
672
|
-
typeof comment.body !== "string" ||
|
|
673
|
-
thread === undefined ||
|
|
674
|
-
typeof thread.id !== "string" ||
|
|
675
|
-
thread.id === "" ||
|
|
676
|
-
typeof thread.path !== "string" ||
|
|
677
|
-
thread.path === "" ||
|
|
678
|
-
typeof thread.startLine !== "number" ||
|
|
679
|
-
thread.startLine <= 0
|
|
680
|
-
) {
|
|
681
|
-
return undefined;
|
|
682
|
-
}
|
|
683
|
-
const startLine = thread.startLine;
|
|
684
|
-
const endLine =
|
|
685
|
-
typeof thread.endLine === "number" && thread.endLine >= startLine
|
|
686
|
-
? thread.endLine
|
|
687
|
-
: startLine;
|
|
688
|
-
const commitSha = thread.version?.headSha;
|
|
689
|
-
const author = comment.author;
|
|
690
|
-
const authorLogin =
|
|
691
|
-
author?.app?.displayName ?? author?.app?.id ?? author?.user?.email;
|
|
692
|
-
// App and service-account actors are machine identities; only a `user`
|
|
693
|
-
// actor is a human PR participant.
|
|
694
|
-
const authorIsBot =
|
|
695
|
-
author === undefined
|
|
696
|
-
? undefined
|
|
697
|
-
: author.app !== undefined || author.serviceAccount !== undefined;
|
|
698
|
-
const authorAppId = author?.app?.id;
|
|
699
|
-
return {
|
|
700
|
-
commentId: comment.id,
|
|
701
|
-
threadId: thread.id,
|
|
702
|
-
resolved: typeof thread.resolvedAt === "string" && thread.resolvedAt !== "",
|
|
703
|
-
file: thread.path,
|
|
704
|
-
startLine,
|
|
705
|
-
endLine,
|
|
706
|
-
body: comment.body,
|
|
707
|
-
...(typeof commitSha === "string" && commitSha !== "" ? { commitSha } : {}),
|
|
708
|
-
...(typeof authorLogin === "string" && authorLogin !== ""
|
|
709
|
-
? { authorLogin }
|
|
710
|
-
: {}),
|
|
711
|
-
...(authorIsBot === undefined ? {} : { authorIsBot }),
|
|
712
|
-
...(typeof authorAppId === "string" && authorAppId !== ""
|
|
713
|
-
? { authorAppId }
|
|
714
|
-
: {}),
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* Lists the PR's inline review threads through the Origin public REST API
|
|
720
|
-
* (`GET …/pulls/{n}/comments`, paged). The flattened comment list is
|
|
721
|
-
* ordered by creation time, so the first comment seen per thread is the
|
|
722
|
-
* thread opener; replies are dropped.
|
|
723
|
-
*/
|
|
724
|
-
export function createHostOriginReviewCommentLister(
|
|
725
|
-
origin: HostOriginReviewClient,
|
|
726
|
-
log?: ReviewCommentLog
|
|
727
|
-
): ReviewCommentListerImplementation {
|
|
728
|
-
return {
|
|
729
|
-
async listReviewComments({ target, signal }) {
|
|
730
|
-
const [owner, repo] = target.repo.split("/", 2);
|
|
731
|
-
if (owner === undefined || repo === undefined || repo === "") {
|
|
732
|
-
log?.(`origin list: unparseable owner/repo "${target.repo}"`);
|
|
733
|
-
return [];
|
|
734
|
-
}
|
|
735
|
-
const basePath = `/v1/origin/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${target.prNumber}/comments`;
|
|
736
|
-
const comments: ListedReviewComment[] = [];
|
|
737
|
-
const seenThreads = new Set<string>();
|
|
738
|
-
let totalComments = 0;
|
|
739
|
-
let pageToken = "";
|
|
740
|
-
for (let page = 0; page < ORIGIN_COMMENTS_MAX_PAGES; page++) {
|
|
741
|
-
signal?.throwIfAborted();
|
|
742
|
-
const query =
|
|
743
|
-
pageToken === ""
|
|
744
|
-
? `?pageSize=${ORIGIN_COMMENTS_PAGE_SIZE}`
|
|
745
|
-
: `?pageSize=${ORIGIN_COMMENTS_PAGE_SIZE}&pageToken=${encodeURIComponent(pageToken)}`;
|
|
746
|
-
const response = (await origin.request({
|
|
747
|
-
method: "GET",
|
|
748
|
-
path: `${basePath}${query}`,
|
|
749
|
-
})) as OriginListCommentsJson | null | undefined;
|
|
750
|
-
for (const comment of response?.comments ?? []) {
|
|
751
|
-
if (comment === null || comment === undefined) {
|
|
752
|
-
continue;
|
|
753
|
-
}
|
|
754
|
-
totalComments += 1;
|
|
755
|
-
const listed = listedCommentFromOriginComment(comment);
|
|
756
|
-
if (listed === undefined || seenThreads.has(listed.threadId)) {
|
|
757
|
-
// Not line-anchored, or a reply on a thread whose opener was
|
|
758
|
-
// already taken (the list is oldest-first).
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
seenThreads.add(listed.threadId);
|
|
762
|
-
comments.push(listed);
|
|
763
|
-
}
|
|
764
|
-
pageToken = response?.nextPageToken ?? "";
|
|
765
|
-
if (pageToken === "") {
|
|
766
|
-
break;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
log?.(
|
|
770
|
-
`origin list: ${target.repo}#${target.prNumber} has ${totalComments} comment(s), ${comments.length} line-anchored thread opener(s)`
|
|
771
|
-
);
|
|
772
|
-
return comments;
|
|
773
|
-
},
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/**
|
|
778
|
-
* Resolves Origin comment threads through the public REST API
|
|
779
|
-
* (`PATCH …/pulls/threads/{thread_id}` with `resolved: true`).
|
|
780
|
-
*/
|
|
781
|
-
export function createHostOriginReviewThreadResolver(
|
|
782
|
-
origin: HostOriginReviewClient,
|
|
783
|
-
log?: ReviewCommentLog
|
|
784
|
-
): ReviewThreadResolverImplementation {
|
|
785
|
-
return {
|
|
786
|
-
async resolveReviewThreads({ target, threadIds, signal }) {
|
|
787
|
-
const uniqueIds = [...new Set(threadIds.filter((id) => id !== ""))];
|
|
788
|
-
if (uniqueIds.length === 0) {
|
|
789
|
-
return skippedResolve("no threads to resolve");
|
|
790
|
-
}
|
|
791
|
-
const [owner, repo] = target.repo.split("/", 2);
|
|
792
|
-
if (owner === undefined || repo === undefined || repo === "") {
|
|
793
|
-
return skippedResolve("unparseable owner/repo from PR URL");
|
|
794
|
-
}
|
|
795
|
-
let resolved = 0;
|
|
796
|
-
let failed = 0;
|
|
797
|
-
for (const threadId of uniqueIds) {
|
|
798
|
-
signal?.throwIfAborted();
|
|
799
|
-
try {
|
|
800
|
-
await origin.request({
|
|
801
|
-
method: "PATCH",
|
|
802
|
-
path: `/v1/origin/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/threads/${encodeURIComponent(threadId)}`,
|
|
803
|
-
body: { resolved: true },
|
|
804
|
-
});
|
|
805
|
-
resolved += 1;
|
|
806
|
-
log?.(`origin resolve: thread ${threadId} resolved`);
|
|
807
|
-
} catch (error) {
|
|
808
|
-
if (signal?.aborted) {
|
|
809
|
-
throw error;
|
|
810
|
-
}
|
|
811
|
-
failed += 1;
|
|
812
|
-
log?.(
|
|
813
|
-
`origin resolve: thread ${threadId} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
return {
|
|
818
|
-
requested: uniqueIds.length,
|
|
819
|
-
resolved,
|
|
820
|
-
failed,
|
|
821
|
-
detail: `resolved ${resolved} of ${uniqueIds.length} thread(s) on ${target.repo}#${target.prNumber} via host Origin (${failed} failed)`,
|
|
822
|
-
};
|
|
823
|
-
},
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
type ParsedGithubPrUrl = {
|
|
828
|
-
repo: string;
|
|
829
|
-
prNumber: number;
|
|
830
|
-
};
|
|
831
|
-
|
|
832
|
-
/**
|
|
833
|
-
* Prepares the provider-neutral review payload used by local `gh` posting.
|
|
834
|
-
*/
|
|
835
|
-
export function prepareReviewCommentsRequest(
|
|
836
|
-
args: ReviewCommentPostArgs
|
|
837
|
-
):
|
|
838
|
-
| { kind: "prepared"; request: ReviewCommentsRequest }
|
|
839
|
-
| { kind: "skipped"; result: ReviewCommentPostResult } {
|
|
840
|
-
const target = parseGithubPrUrl(args.prUrl);
|
|
841
|
-
if (target.kind === "invalid") {
|
|
842
|
-
return { kind: "skipped", result: skipped(target.detail) };
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
return {
|
|
846
|
-
kind: "prepared",
|
|
847
|
-
request: {
|
|
848
|
-
provider: "github",
|
|
849
|
-
repo: target.value.repo,
|
|
850
|
-
prNumber: target.value.prNumber,
|
|
851
|
-
headSha: args.reviewedHeadSha,
|
|
852
|
-
message: args.message,
|
|
853
|
-
comments: args.comments,
|
|
854
|
-
cleanupPrevious: args.cleanupPrevious ?? true,
|
|
855
|
-
},
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
/**
|
|
860
|
-
* Posts a PR review with the local `gh` credentials (`gh auth` / GITHUB_TOKEN /
|
|
861
|
-
* GH_TOKEN). Does not use the security-bugbot comments service.
|
|
862
|
-
*/
|
|
863
|
-
export function createLocalGhReviewPublisher(): ReviewCommentPublisherImplementation {
|
|
864
|
-
return {
|
|
865
|
-
async postReview(args) {
|
|
866
|
-
return postReviewViaLocalGh(args);
|
|
867
|
-
},
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Posts a PR review through the host GitHub client (Cursor account lease,
|
|
873
|
-
* App installation token, or PAT). Prefer this on Cursor-deployed agents —
|
|
874
|
-
* no separate GitHub write secret is required when `githubChannel({
|
|
875
|
-
* cursorAccount })` is bound.
|
|
876
|
-
*/
|
|
877
|
-
export function createHostGithubReviewPublisher(
|
|
878
|
-
github: HostGitHubClient
|
|
879
|
-
): ReviewCommentPublisherImplementation {
|
|
880
|
-
return {
|
|
881
|
-
async postReview(args) {
|
|
882
|
-
return postReviewViaHostGithub(github, args);
|
|
883
|
-
},
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
/**
|
|
888
|
-
* Pick the publisher for `prUrl`'s SCM. GitHub falls back to local `gh`
|
|
889
|
-
* when no host client is bound (CLI). Origin has no local CLI publisher —
|
|
890
|
-
* returns undefined unless an Origin REST client is provided.
|
|
891
|
-
*/
|
|
892
|
-
export function createReviewCommentPublisher(args: {
|
|
893
|
-
prUrl: string;
|
|
894
|
-
github?: HostGitHubClient;
|
|
895
|
-
origin?: HostOriginReviewClient;
|
|
896
|
-
}): ReviewCommentPublisherImplementation | undefined {
|
|
897
|
-
const target = resolveScmReviewTarget(args.prUrl);
|
|
898
|
-
if (target === undefined) {
|
|
899
|
-
return undefined;
|
|
900
|
-
}
|
|
901
|
-
switch (target.provider) {
|
|
902
|
-
case "github":
|
|
903
|
-
return args.github !== undefined
|
|
904
|
-
? createHostGithubReviewPublisher(args.github)
|
|
905
|
-
: createLocalGhReviewPublisher();
|
|
906
|
-
case "origin":
|
|
907
|
-
return args.origin === undefined
|
|
908
|
-
? undefined
|
|
909
|
-
: createHostOriginReviewPublisher(args.origin);
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
/**
|
|
914
|
-
* Posts a COMMENT-verdict Origin review through the public REST API.
|
|
915
|
-
*
|
|
916
|
-
* Inline comments post as diff-anchored threads via the review's
|
|
917
|
-
* `comments` field (`ReviewCommentInput` with an `InlineCommentAnchor`).
|
|
918
|
-
* Origin rejects the whole request with INVALID_ARGUMENT when any anchor
|
|
919
|
-
* is off the reviewed version's diff — the caller's body-only retry (the
|
|
920
|
-
* same fallback GitHub's 422 takes) then folds the findings into the
|
|
921
|
-
* review body.
|
|
922
|
-
*/
|
|
923
|
-
export function createHostOriginReviewPublisher(
|
|
924
|
-
origin: HostOriginReviewClient
|
|
925
|
-
): ReviewCommentPublisherImplementation {
|
|
926
|
-
return {
|
|
927
|
-
async postReview(args) {
|
|
928
|
-
return postReviewViaHostOrigin(origin, args);
|
|
929
|
-
},
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
async function postReviewViaHostGithub(
|
|
934
|
-
github: HostGitHubClient,
|
|
935
|
-
args: ReviewCommentPostArgs
|
|
936
|
-
): Promise<ReviewCommentPostResult> {
|
|
937
|
-
const prepared = prepareReviewCommentsRequest(args);
|
|
938
|
-
if (prepared.kind === "skipped") {
|
|
939
|
-
return {
|
|
940
|
-
...prepared.result,
|
|
941
|
-
publisher: "host_github",
|
|
942
|
-
};
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
const { request } = prepared;
|
|
946
|
-
const [owner, repo] = request.repo.split("/", 2);
|
|
947
|
-
if (owner === undefined || repo === undefined || repo === "") {
|
|
948
|
-
return skippedHost("unparseable owner/repo from PR URL");
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
try {
|
|
952
|
-
args.signal?.throwIfAborted();
|
|
953
|
-
const octokit = await github.getOctokit();
|
|
954
|
-
args.signal?.throwIfAborted();
|
|
955
|
-
const response = await octokit.rest.pulls.createReview({
|
|
956
|
-
owner,
|
|
957
|
-
repo,
|
|
958
|
-
pull_number: request.prNumber,
|
|
959
|
-
commit_id: request.headSha,
|
|
960
|
-
body: request.message,
|
|
961
|
-
event: "COMMENT",
|
|
962
|
-
comments: request.comments.map(toGithubPullRequestReviewComment),
|
|
963
|
-
});
|
|
964
|
-
const htmlUrl =
|
|
965
|
-
typeof response.data.html_url === "string"
|
|
966
|
-
? response.data.html_url
|
|
967
|
-
: undefined;
|
|
968
|
-
const inlineCommentCount = request.comments.length;
|
|
969
|
-
return {
|
|
970
|
-
posted: true,
|
|
971
|
-
publisher: "host_github",
|
|
972
|
-
mode:
|
|
973
|
-
inlineCommentCount > 0
|
|
974
|
-
? "review_with_inline_comments"
|
|
975
|
-
: "review_body_only",
|
|
976
|
-
inlineCommentCount,
|
|
977
|
-
detail: htmlUrl
|
|
978
|
-
? `posted via host GitHub: ${htmlUrl}`
|
|
979
|
-
: `posted via host GitHub with ${inlineCommentCount} inline comment(s)`,
|
|
980
|
-
postedComments: await readPostedComments({
|
|
981
|
-
octokit,
|
|
982
|
-
owner,
|
|
983
|
-
repo,
|
|
984
|
-
prNumber: request.prNumber,
|
|
985
|
-
reviewId: response.data.id,
|
|
986
|
-
expected: inlineCommentCount,
|
|
987
|
-
signal: args.signal,
|
|
988
|
-
}),
|
|
989
|
-
};
|
|
990
|
-
} catch (error) {
|
|
991
|
-
if (args.signal?.aborted) {
|
|
992
|
-
throw error;
|
|
993
|
-
}
|
|
994
|
-
return skippedHost(
|
|
995
|
-
`host GitHub review post failed: ${error instanceof Error ? error.message : String(error)}`
|
|
996
|
-
);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* Ids of the inline comments a just-created review owns.
|
|
1002
|
-
*
|
|
1003
|
-
* `pulls.createReview` returns the review, not its comments.
|
|
1004
|
-
* `listCommentsForReview` either omits those rows or returns them with
|
|
1005
|
-
* `line` / `original_line` unset, so the line filter drops every id, the
|
|
1006
|
-
* store records no findings, and the next run cannot judge or resolve
|
|
1007
|
-
* those threads. Read the PR-wide list instead and keep only this
|
|
1008
|
-
* review's rows. Newest-first so a busy PR's older comments cannot push
|
|
1009
|
-
* ours past the page cap.
|
|
1010
|
-
* Best-effort: any failure yields an empty list, which costs the next run
|
|
1011
|
-
* its prior findings rather than risking a wrong id. Never throws — the
|
|
1012
|
-
* review is already posted by this point.
|
|
1013
|
-
*/
|
|
1014
|
-
async function readPostedComments(args: {
|
|
1015
|
-
octokit: Awaited<ReturnType<HostGitHubClient["getOctokit"]>>;
|
|
1016
|
-
owner: string;
|
|
1017
|
-
repo: string;
|
|
1018
|
-
prNumber: number;
|
|
1019
|
-
reviewId: number;
|
|
1020
|
-
expected: number;
|
|
1021
|
-
signal?: AbortSignal;
|
|
1022
|
-
}): Promise<PostedReviewComment[]> {
|
|
1023
|
-
if (args.expected === 0) {
|
|
1024
|
-
return [];
|
|
1025
|
-
}
|
|
1026
|
-
try {
|
|
1027
|
-
const posted: PostedReviewComment[] = [];
|
|
1028
|
-
for (let page = 1; page <= REVIEW_COMMENTS_MAX_PAGES; page++) {
|
|
1029
|
-
args.signal?.throwIfAborted();
|
|
1030
|
-
const response = await args.octokit.rest.pulls.listReviewComments({
|
|
1031
|
-
owner: args.owner,
|
|
1032
|
-
repo: args.repo,
|
|
1033
|
-
pull_number: args.prNumber,
|
|
1034
|
-
sort: "created",
|
|
1035
|
-
direction: "desc",
|
|
1036
|
-
per_page: REVIEW_COMMENTS_PAGE_SIZE,
|
|
1037
|
-
page,
|
|
1038
|
-
});
|
|
1039
|
-
for (const comment of response.data) {
|
|
1040
|
-
if (comment.pull_request_review_id !== args.reviewId) {
|
|
1041
|
-
continue;
|
|
1042
|
-
}
|
|
1043
|
-
const line = comment.original_line ?? comment.line;
|
|
1044
|
-
if (line === null || line === undefined) {
|
|
1045
|
-
continue;
|
|
1046
|
-
}
|
|
1047
|
-
posted.push({ id: comment.id, file: comment.path, line });
|
|
1048
|
-
}
|
|
1049
|
-
if (
|
|
1050
|
-
posted.length >= args.expected ||
|
|
1051
|
-
response.data.length < REVIEW_COMMENTS_PAGE_SIZE
|
|
1052
|
-
) {
|
|
1053
|
-
break;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
return posted;
|
|
1057
|
-
} catch {
|
|
1058
|
-
return [];
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
async function postReviewViaLocalGh(
|
|
1063
|
-
args: ReviewCommentPostArgs
|
|
1064
|
-
): Promise<ReviewCommentPostResult> {
|
|
1065
|
-
const prepared = prepareReviewCommentsRequest(args);
|
|
1066
|
-
if (prepared.kind === "skipped") {
|
|
1067
|
-
return prepared.result;
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const { request } = prepared;
|
|
1071
|
-
const path = `repos/${request.repo}/pulls/${request.prNumber}/reviews`;
|
|
1072
|
-
const payload = {
|
|
1073
|
-
commit_id: request.headSha,
|
|
1074
|
-
body: request.message,
|
|
1075
|
-
event: "COMMENT",
|
|
1076
|
-
comments: request.comments.map(toGithubReviewComment),
|
|
1077
|
-
};
|
|
1078
|
-
|
|
1079
|
-
try {
|
|
1080
|
-
args.signal?.throwIfAborted();
|
|
1081
|
-
const response = await ghApiPostJson(path, payload, args.signal);
|
|
1082
|
-
const htmlUrl =
|
|
1083
|
-
typeof response.html_url === "string" ? response.html_url : undefined;
|
|
1084
|
-
const inlineCommentCount = request.comments.length;
|
|
1085
|
-
return {
|
|
1086
|
-
posted: true,
|
|
1087
|
-
publisher: "local_gh",
|
|
1088
|
-
mode:
|
|
1089
|
-
inlineCommentCount > 0
|
|
1090
|
-
? "review_with_inline_comments"
|
|
1091
|
-
: "review_body_only",
|
|
1092
|
-
inlineCommentCount,
|
|
1093
|
-
detail: htmlUrl
|
|
1094
|
-
? `posted via local gh: ${htmlUrl}`
|
|
1095
|
-
: `posted via local gh with ${inlineCommentCount} inline comment(s)`,
|
|
1096
|
-
};
|
|
1097
|
-
} catch (error) {
|
|
1098
|
-
if (args.signal?.aborted) {
|
|
1099
|
-
throw error;
|
|
1100
|
-
}
|
|
1101
|
-
return skipped(
|
|
1102
|
-
`local gh review post failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
type GithubPullRequestReviewComment = {
|
|
1108
|
-
path: string;
|
|
1109
|
-
body: string;
|
|
1110
|
-
line: number;
|
|
1111
|
-
side: "RIGHT";
|
|
1112
|
-
start_line?: number;
|
|
1113
|
-
start_side?: "RIGHT";
|
|
1114
|
-
};
|
|
1115
|
-
|
|
1116
|
-
function toGithubReviewComment(
|
|
1117
|
-
comment: ReviewInlineComment
|
|
1118
|
-
): Record<string, string | number> {
|
|
1119
|
-
return toGithubPullRequestReviewComment(comment);
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
function toGithubPullRequestReviewComment(
|
|
1123
|
-
comment: ReviewInlineComment
|
|
1124
|
-
): GithubPullRequestReviewComment {
|
|
1125
|
-
const body: GithubPullRequestReviewComment = {
|
|
1126
|
-
path: comment.file,
|
|
1127
|
-
body: comment.body,
|
|
1128
|
-
line: comment.line,
|
|
1129
|
-
side: "RIGHT",
|
|
1130
|
-
};
|
|
1131
|
-
if (
|
|
1132
|
-
comment.start_line !== undefined &&
|
|
1133
|
-
comment.start_line > 0 &&
|
|
1134
|
-
comment.start_line !== comment.line
|
|
1135
|
-
) {
|
|
1136
|
-
body.start_line = comment.start_line;
|
|
1137
|
-
body.start_side = "RIGHT";
|
|
1138
|
-
}
|
|
1139
|
-
return body;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
function skipped(detail: string): ReviewCommentPostResult {
|
|
1143
|
-
return {
|
|
1144
|
-
posted: false,
|
|
1145
|
-
publisher: "local_gh",
|
|
1146
|
-
mode: "skipped",
|
|
1147
|
-
inlineCommentCount: 0,
|
|
1148
|
-
detail,
|
|
1149
|
-
};
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
function skippedHost(detail: string): ReviewCommentPostResult {
|
|
1153
|
-
return {
|
|
1154
|
-
posted: false,
|
|
1155
|
-
publisher: "host_github",
|
|
1156
|
-
mode: "skipped",
|
|
1157
|
-
inlineCommentCount: 0,
|
|
1158
|
-
detail,
|
|
1159
|
-
};
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
function skippedOrigin(detail: string): ReviewCommentPostResult {
|
|
1163
|
-
return {
|
|
1164
|
-
posted: false,
|
|
1165
|
-
publisher: "host_origin",
|
|
1166
|
-
mode: "skipped",
|
|
1167
|
-
inlineCommentCount: 0,
|
|
1168
|
-
detail,
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
async function postReviewViaHostOrigin(
|
|
1173
|
-
origin: HostOriginReviewClient,
|
|
1174
|
-
args: ReviewCommentPostArgs
|
|
1175
|
-
): Promise<ReviewCommentPostResult> {
|
|
1176
|
-
const target = resolveScmReviewTarget(args.prUrl);
|
|
1177
|
-
if (target === undefined || target.provider !== "origin") {
|
|
1178
|
-
return skippedOrigin("host Origin publisher only supports Origin PRs");
|
|
1179
|
-
}
|
|
1180
|
-
const [owner, repo] = target.repo.split("/", 2);
|
|
1181
|
-
if (owner === undefined || repo === undefined || repo === "") {
|
|
1182
|
-
return skippedOrigin("unparseable owner/repo from PR URL");
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
try {
|
|
1186
|
-
args.signal?.throwIfAborted();
|
|
1187
|
-
await origin.request({
|
|
1188
|
-
method: "POST",
|
|
1189
|
-
path: `/v1/origin/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${target.prNumber}/reviews`,
|
|
1190
|
-
body: {
|
|
1191
|
-
verdict: "comment",
|
|
1192
|
-
body: args.message,
|
|
1193
|
-
comments: args.comments.map(toOriginReviewCommentInput),
|
|
1194
|
-
},
|
|
1195
|
-
});
|
|
1196
|
-
const inlineCommentCount = args.comments.length;
|
|
1197
|
-
return {
|
|
1198
|
-
posted: true,
|
|
1199
|
-
publisher: "host_origin",
|
|
1200
|
-
mode:
|
|
1201
|
-
inlineCommentCount > 0
|
|
1202
|
-
? "review_with_inline_comments"
|
|
1203
|
-
: "review_body_only",
|
|
1204
|
-
inlineCommentCount,
|
|
1205
|
-
detail: `posted Origin review on ${target.repo}#${target.prNumber} with ${inlineCommentCount} inline comment(s)`,
|
|
1206
|
-
};
|
|
1207
|
-
} catch (error) {
|
|
1208
|
-
if (args.signal?.aborted) {
|
|
1209
|
-
throw error;
|
|
1210
|
-
}
|
|
1211
|
-
return skippedOrigin(
|
|
1212
|
-
`host Origin review post failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
type OriginReviewCommentInput = {
|
|
1218
|
-
body: string;
|
|
1219
|
-
inline: {
|
|
1220
|
-
path: string;
|
|
1221
|
-
side: "right";
|
|
1222
|
-
startLine: number;
|
|
1223
|
-
endLine?: number;
|
|
1224
|
-
};
|
|
1225
|
-
};
|
|
1226
|
-
|
|
1227
|
-
/** Provider-neutral inline comment → Origin `ReviewCommentInput`. */
|
|
1228
|
-
function toOriginReviewCommentInput(
|
|
1229
|
-
comment: ReviewInlineComment
|
|
1230
|
-
): OriginReviewCommentInput {
|
|
1231
|
-
const startLine =
|
|
1232
|
-
comment.start_line !== undefined &&
|
|
1233
|
-
comment.start_line > 0 &&
|
|
1234
|
-
comment.start_line < comment.line
|
|
1235
|
-
? comment.start_line
|
|
1236
|
-
: comment.line;
|
|
1237
|
-
return {
|
|
1238
|
-
body: comment.body,
|
|
1239
|
-
inline: {
|
|
1240
|
-
path: comment.file,
|
|
1241
|
-
side: "right",
|
|
1242
|
-
startLine,
|
|
1243
|
-
...(startLine === comment.line ? {} : { endLine: comment.line }),
|
|
1244
|
-
},
|
|
1245
|
-
};
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
function skippedResolve(detail: string): ResolveReviewThreadsResult {
|
|
1249
|
-
return {
|
|
1250
|
-
requested: 0,
|
|
1251
|
-
resolved: 0,
|
|
1252
|
-
failed: 0,
|
|
1253
|
-
detail,
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
function parseGithubPrUrl(
|
|
1258
|
-
prUrl: string
|
|
1259
|
-
):
|
|
1260
|
-
| { kind: "valid"; value: ParsedGithubPrUrl }
|
|
1261
|
-
| { kind: "invalid"; detail: string } {
|
|
1262
|
-
let url: URL;
|
|
1263
|
-
try {
|
|
1264
|
-
url = new URL(prUrl);
|
|
1265
|
-
} catch (error) {
|
|
1266
|
-
return {
|
|
1267
|
-
kind: "invalid",
|
|
1268
|
-
detail: `unparseable PR URL: ${error instanceof Error ? error.message : String(error)}`,
|
|
1269
|
-
};
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
if (url.hostname !== "github.com") {
|
|
1273
|
-
return {
|
|
1274
|
-
kind: "invalid",
|
|
1275
|
-
detail: "local gh publisher only supports github.com PRs",
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
const parts = url.pathname.split("/").filter(Boolean);
|
|
1280
|
-
if (parts.length !== 4 || parts[2] !== "pull") {
|
|
1281
|
-
return {
|
|
1282
|
-
kind: "invalid",
|
|
1283
|
-
detail:
|
|
1284
|
-
"unparseable PR URL: expected https://github.com/{owner}/{repo}/pull/{number}",
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
const prNumber = Number(parts[3]);
|
|
1289
|
-
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
1290
|
-
return {
|
|
1291
|
-
kind: "invalid",
|
|
1292
|
-
detail:
|
|
1293
|
-
"unparseable PR URL: pull request number must be a positive integer",
|
|
1294
|
-
};
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
return {
|
|
1298
|
-
kind: "valid",
|
|
1299
|
-
value: {
|
|
1300
|
-
repo: `${parts[0]}/${parts[1]}`,
|
|
1301
|
-
prNumber,
|
|
1302
|
-
},
|
|
1303
|
-
};
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
async function ghApiPostJson(
|
|
1307
|
-
path: string,
|
|
1308
|
-
payload: unknown,
|
|
1309
|
-
signal?: AbortSignal
|
|
1310
|
-
): Promise<Record<string, unknown>> {
|
|
1311
|
-
return new Promise((resolvePromise, rejectPromise) => {
|
|
1312
|
-
const child = spawn(
|
|
1313
|
-
"gh",
|
|
1314
|
-
["api", path, "--method", "POST", "--input", "-"],
|
|
1315
|
-
{
|
|
1316
|
-
env: { ...process.env, GH_PROMPT_DISABLED: "1" },
|
|
1317
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1318
|
-
}
|
|
1319
|
-
);
|
|
1320
|
-
let stdout = "";
|
|
1321
|
-
let stderr = "";
|
|
1322
|
-
const onAbort = () => {
|
|
1323
|
-
child.kill("SIGTERM");
|
|
1324
|
-
rejectPromise(signal?.reason ?? new Error("aborted"));
|
|
1325
|
-
};
|
|
1326
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1327
|
-
child.stdout.on("data", (chunk) => {
|
|
1328
|
-
stdout += String(chunk);
|
|
1329
|
-
});
|
|
1330
|
-
child.stderr.on("data", (chunk) => {
|
|
1331
|
-
stderr += String(chunk);
|
|
1332
|
-
});
|
|
1333
|
-
child.on("error", (error) => {
|
|
1334
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1335
|
-
rejectPromise(error);
|
|
1336
|
-
});
|
|
1337
|
-
child.on("close", (code) => {
|
|
1338
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1339
|
-
if (code !== 0) {
|
|
1340
|
-
rejectPromise(
|
|
1341
|
-
new Error(
|
|
1342
|
-
`gh api ${path} exited with ${code}: ${stderr.trim().slice(0, 2000)}`
|
|
1343
|
-
)
|
|
1344
|
-
);
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
if (!stdout.trim()) {
|
|
1348
|
-
resolvePromise({});
|
|
1349
|
-
return;
|
|
1350
|
-
}
|
|
1351
|
-
try {
|
|
1352
|
-
resolvePromise(JSON.parse(stdout) as Record<string, unknown>);
|
|
1353
|
-
} catch (error) {
|
|
1354
|
-
rejectPromise(
|
|
1355
|
-
new Error(
|
|
1356
|
-
`gh api ${path} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
1357
|
-
)
|
|
1358
|
-
);
|
|
1359
|
-
}
|
|
1360
|
-
});
|
|
1361
|
-
child.stdin.end(JSON.stringify(payload));
|
|
1362
|
-
});
|
|
1363
|
-
}
|