@cursor/july 0.1.94 → 0.1.96
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 +3 -4
- package/dist/channels/deployments/types.d.ts +1 -1
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/deployments/types.js +1 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +14 -2
- package/dist/channels/github/types.d.ts +18 -3
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/api.d.ts +59 -13
- package/dist/channels/origin/api.d.ts.map +1 -1
- package/dist/channels/origin/api.js +168 -27
- package/dist/channels/origin/binding.d.ts +2 -2
- package/dist/channels/origin/checks.d.ts +6 -6
- package/dist/channels/origin/checks.js +6 -6
- package/dist/channels/origin/cursor-account.d.ts +5 -3
- package/dist/channels/origin/cursor-account.d.ts.map +1 -1
- package/dist/channels/origin/cursor-account.js +5 -3
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +11 -3
- package/dist/channels/origin/origin-webhook.d.ts +11 -1
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -1
- package/dist/channels/origin/origin-webhook.js +23 -3
- package/dist/channels/origin/types.d.ts +8 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/api.d.ts +9 -0
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +36 -5
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +17 -46
- package/dist/channels/slack/dispatch.d.ts +7 -1
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +47 -23
- package/dist/channels/slack/inbound.d.ts +8 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +32 -0
- package/dist/channels/slack/live-delivery.d.ts +26 -29
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +172 -73
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +5 -1
- package/dist/channels/slack/nudge-carrier.d.ts +2 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
- package/dist/channels/slack/nudge-carrier.js +48 -1
- package/dist/channels/slack/pr-ref.d.ts +7 -1
- package/dist/channels/slack/pr-ref.d.ts.map +1 -1
- package/dist/channels/slack/pr-ref.js +42 -23
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +2 -2
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CFDEas4I.js → app.jXleCmOc.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.B1IIYpYS.js → VPLocalSearchBox.8LhyglIT.js} +1 -1
- package/dist/docs/assets/chunks/{theme.Ct4NSiLm.js → theme.9ged-pr2.js} +2 -2
- package/dist/docs/assets/index.md.CI93Ltlb.js +5 -0
- package/dist/docs/assets/{index.md.DRakGHFe.lean.js → index.md.CI93Ltlb.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.js → reference_cli.md.CCnG-nJx.js} +4 -2
- package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.lean.js → reference_cli.md.CCnG-nJx.lean.js} +1 -1
- package/dist/docs/assets/{reference_skills.md.8son6Hjm.js → reference_skills.md.BFW9retM.js} +3 -3
- package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.js → templates_agentic-owners.md.BZSH4N9z.js} +1 -1
- package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.js +2 -0
- package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.lean.js +1 -0
- package/dist/docs/assets/{templates_demo.md.DhFcWN6j.js → templates_demo.md.BeQX9V3H.js} +1 -1
- package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.js → templates_pr-autofixer.md.x5zl6-GT.js} +2 -2
- package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.lean.js → templates_pr-autofixer.md.x5zl6-GT.lean.js} +1 -1
- package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.js +4 -0
- package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.lean.js +1 -0
- package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.js → templates_security-reviewer.md.lshxbCLK.js} +2 -2
- package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.lean.js → templates_security-reviewer.md.lshxbCLK.lean.js} +1 -1
- package/dist/docs/assets/{templates_triage.md.CVlpctKS.js → templates_triage.md.Co4UNzkZ.js} +3 -3
- package/dist/docs/assets/{templates_triage.md.CVlpctKS.lean.js → templates_triage.md.Co4UNzkZ.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- 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 +5 -1
- package/dist/docs/llms-full.txt +189 -26
- package/dist/docs/llms.txt +2 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +8 -6
- package/dist/docs/reference/cli.md +4 -1
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +7 -7
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +7 -7
- package/dist/docs/templates/agentic-owners.md +1 -1
- package/dist/docs/templates/code-wiki.html +28 -0
- package/dist/docs/templates/code-wiki.md +91 -0
- package/dist/docs/templates/demo.html +6 -6
- package/dist/docs/templates/demo.md +3 -2
- package/dist/docs/templates/pr-autofixer.html +6 -6
- package/dist/docs/templates/pr-autofixer.md +7 -13
- package/dist/docs/templates/security-help.html +30 -0
- package/dist/docs/templates/security-help.md +65 -0
- package/dist/docs/templates/security-reviewer.html +6 -6
- package/dist/docs/templates/security-reviewer.md +1 -2
- package/dist/docs/templates/triage.html +7 -7
- package/dist/docs/templates/triage.md +2 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/files-backends/cursor-hosted.d.ts +26 -0
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +32 -4
- package/dist/files.d.ts +2 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +5 -0
- package/dist/internal/advertise-tools.d.ts +2 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +15 -1
- package/dist/internal/bounded.d.ts +7 -0
- package/dist/internal/bounded.d.ts.map +1 -0
- package/dist/internal/bounded.js +32 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +142 -7
- package/dist/internal/conversation-mirror.d.ts +85 -0
- package/dist/internal/conversation-mirror.d.ts.map +1 -0
- package/dist/internal/conversation-mirror.js +312 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +22 -48
- package/dist/internal/cursor/session-token.d.ts +45 -0
- package/dist/internal/cursor/session-token.d.ts.map +1 -0
- package/dist/internal/cursor/session-token.js +99 -0
- package/dist/internal/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -0
- package/dist/internal/deferred-channel-session.d.ts +20 -0
- package/dist/internal/deferred-channel-session.d.ts.map +1 -0
- package/dist/internal/deferred-channel-session.js +62 -0
- package/dist/internal/deploy-client.d.ts +13 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +11 -1
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +11 -5
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +22 -2
- package/dist/internal/framework-file-storage.d.ts +61 -0
- package/dist/internal/framework-file-storage.d.ts.map +1 -0
- package/dist/internal/framework-file-storage.js +204 -0
- package/dist/internal/framework-storage-selection.d.ts +38 -16
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +78 -17
- package/dist/internal/hosted-admission-context.d.ts +20 -0
- package/dist/internal/hosted-admission-context.d.ts.map +1 -0
- package/dist/internal/hosted-admission-context.js +31 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +5 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +33 -1
- package/dist/internal/hosted-delivery.d.ts +11 -2
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +79 -35
- package/dist/internal/hosted-managed-automation.d.ts +30 -0
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -0
- package/dist/internal/hosted-managed-automation.js +58 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +2 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -1
- package/dist/internal/install-cursor-skills.js +1 -3
- package/dist/internal/managed-delivery.d.ts +80 -0
- package/dist/internal/managed-delivery.d.ts.map +1 -0
- package/dist/internal/managed-delivery.js +86 -0
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +10 -98
- package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
- package/dist/internal/runtime-dispatch-runner.js +7 -0
- package/dist/internal/scm/pr-url.d.ts +14 -0
- package/dist/internal/scm/pr-url.d.ts.map +1 -0
- package/dist/internal/scm/pr-url.js +65 -0
- package/dist/internal/sdk-runner.d.ts +25 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +70 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +11 -2
- package/dist/internal/session-engine.d.ts +13 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +71 -3
- package/dist/internal/steering.d.ts +15 -0
- package/dist/internal/steering.d.ts.map +1 -0
- package/dist/internal/steering.js +18 -0
- package/dist/playground/assets/index-B1DDvBeC.css +1 -0
- package/dist/playground/assets/{index-B3JCyigB.js → index-Cx9CclK_.js} +47 -47
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +8 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +4 -3
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +5 -1
- package/docs/reference/cli.md +4 -1
- package/docs/templates/agentic-owners.md +1 -1
- package/docs/templates/code-wiki.md +96 -0
- package/docs/templates/demo.md +3 -2
- package/docs/templates/pr-autofixer.md +7 -13
- package/docs/templates/security-help.md +70 -0
- package/docs/templates/security-reviewer.md +1 -2
- package/docs/templates/triage.md +2 -6
- package/package.json +2 -2
- package/skills/ab/SKILL.md +13 -97
- package/skills/create-agent/SKILL.md +123 -223
- package/skills/debug/SKILL.md +34 -68
- package/skills/evals/SKILL.md +55 -180
- package/skills/framework-map/SKILL.md +83 -145
- package/skills/github/SKILL.md +53 -111
- package/skills/hillclimb/SKILL.md +40 -163
- package/skills/mcp-auth/SKILL.md +48 -107
- package/skills/otel/SKILL.md +22 -68
- package/skills/setup-slack/SKILL.md +77 -208
- package/src/channels/deployments/types.ts +1 -0
- package/src/channels/github/github-channel.ts +23 -8
- package/src/channels/github/types.ts +19 -2
- package/src/channels/origin/api.ts +242 -30
- package/src/channels/origin/binding.ts +2 -2
- package/src/channels/origin/checks.ts +6 -6
- package/src/channels/origin/cursor-account.ts +5 -3
- package/src/channels/origin/origin-channel.ts +13 -1
- package/src/channels/origin/origin-webhook.ts +27 -3
- package/src/channels/origin/types.ts +8 -0
- package/src/channels/slack/api.ts +41 -5
- package/src/channels/slack/defaults.ts +15 -49
- package/src/channels/slack/dispatch.ts +56 -22
- package/src/channels/slack/inbound.ts +43 -0
- package/src/channels/slack/live-delivery.ts +235 -114
- package/src/channels/slack/manifest.ts +5 -1
- package/src/channels/slack/nudge-carrier.ts +48 -1
- package/src/channels/slack/pr-ref.ts +56 -25
- package/src/channels/slack/slack-channel.ts +2 -0
- package/src/files-backends/cursor-hosted.ts +41 -2
- package/src/files.ts +11 -0
- package/src/internal/advertise-tools.ts +18 -2
- package/src/internal/bounded.ts +28 -0
- package/src/internal/cli-deploy.ts +183 -7
- package/src/internal/conversation-mirror.ts +406 -0
- package/src/internal/cursor/backend-client.ts +34 -58
- package/src/internal/cursor/session-token.ts +115 -0
- package/src/internal/cursor-agent-template.ts +1 -0
- package/src/internal/deferred-channel-session.ts +61 -0
- package/src/internal/deploy-client.ts +24 -1
- package/src/internal/deploy-manifest.ts +10 -5
- package/src/internal/discovery.ts +25 -2
- package/src/internal/framework-file-storage.ts +222 -0
- package/src/internal/framework-storage-selection.ts +101 -19
- package/src/internal/hosted-admission-context.ts +37 -0
- package/src/internal/hosted-delivery-protocol.ts +44 -1
- package/src/internal/hosted-delivery.ts +105 -40
- package/src/internal/hosted-managed-automation.ts +72 -0
- package/src/internal/init-project.ts +2 -0
- package/src/internal/install-cursor-skills.ts +1 -4
- package/src/internal/managed-delivery.ts +125 -0
- package/src/internal/review-comments.ts +10 -113
- package/src/internal/runtime-dispatch-runner.ts +10 -0
- package/src/internal/scm/pr-url.ts +95 -0
- package/src/internal/sdk-runner.ts +103 -0
- package/src/internal/server.ts +12 -0
- package/src/internal/session-engine.ts +76 -4
- package/src/internal/steering.ts +17 -0
- package/src/storage-backends/cursor-hosted.ts +10 -1
- package/src/types.ts +2 -1
- package/templates/agentic-owners/README.md +1 -1
- package/templates/agentic-owners/agent/agent.ts +0 -10
- package/templates/agentic-owners/agent/channels/github.ts +5 -14
- package/templates/agentic-owners/agent/lib/config.ts +0 -8
- package/templates/agentic-owners/agent/lib/review.ts +2 -15
- package/templates/agentic-owners/agent/tools/record_review.ts +2 -4
- package/templates/code-wiki/README.md +34 -0
- package/templates/code-wiki/agent/agent.ts +8 -0
- package/templates/code-wiki/agent/channels/github.ts +144 -0
- package/templates/code-wiki/agent/instructions.md +39 -0
- package/templates/code-wiki/agent/lib/repos.ts +5 -0
- package/templates/code-wiki/agent/lib/wiki.ts +22 -0
- package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
- package/templates/code-wiki/evals/evals.config.ts +5 -0
- package/templates/code-wiki/evals/wiki.eval.ts +132 -0
- package/templates/code-wiki/init.json +25 -0
- package/templates/code-wiki/package.json +18 -0
- package/templates/code-wiki/tsconfig.json +12 -0
- package/templates/demo/agent/agent.ts +0 -10
- package/templates/pr-autofixer/README.md +0 -2
- package/templates/pr-autofixer/agent/agent.ts +0 -11
- package/templates/pr-autofixer/agent/channels/slack.ts +0 -7
- package/templates/pr-autofixer/agent/lib/pr-state.ts +5 -17
- package/templates/pr-autofixer/agent/lib/repos.ts +0 -1
- package/templates/security-help/README.md +2 -2
- package/templates/security-help/agent/agent.ts +1 -2
- package/templates/security-help/agent/channels/slack.ts +0 -3
- package/templates/security-help/agent/instructions.md +9 -10
- package/templates/security-help/agent/skills/access-request.md +1 -1
- package/templates/security-help/agent/skills/faq.md +31 -0
- package/templates/security-help/agent/skills/security-playbooks.md +1 -1
- package/templates/security-help/package.json +1 -2
- package/templates/security-reviewer/agent/agent.ts +0 -10
- package/templates/triage/agent/agent.ts +0 -10
- package/templates/triage/agent/channels/queue.ts +1 -1
- package/templates/triage/agent/channels/webhook.ts +1 -3
- package/dist/docs/assets/chunks/@localSearchIndexroot.DU3U2Ij2.js +0 -1
- package/dist/docs/assets/index.md.DRakGHFe.js +0 -5
- package/dist/internal/deployment-scm-event-stream.d.ts +0 -116
- package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
- package/dist/internal/deployment-scm-event-stream.js +0 -474
- package/dist/playground/assets/index-D9N7-q97.css +0 -1
- package/src/internal/deployment-scm-event-stream.ts +0 -593
- package/templates/security-help/agent/knowledge/faq/approvals.md +0 -5
- package/templates/security-help/agent/knowledge/faq/channels.md +0 -6
- package/templates/security-help/agent/knowledge/faq/phishing.md +0 -10
- package/templates/security-help/agent/skills/security-first-pass.md +0 -15
- /package/dist/docs/assets/{reference_skills.md.8son6Hjm.lean.js → reference_skills.md.BFW9retM.lean.js} +0 -0
- /package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.lean.js → templates_agentic-owners.md.BZSH4N9z.lean.js} +0 -0
- /package/dist/docs/assets/{templates_demo.md.DhFcWN6j.lean.js → templates_demo.md.BeQX9V3H.lean.js} +0 -0
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Origin public REST helpers (`https://api.cursor.com/v1/origin/...`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* JWT → `/v1/origin/app/installations/{id}/access_tokens`). This module
|
|
7
|
-
* consolidates that flow so agents configure credentials once and call
|
|
8
|
-
* {@link createOriginClient} — the GitHub-channel analogue of
|
|
4
|
+
* This module consolidates Origin auth so agents configure credentials once
|
|
5
|
+
* and call {@link createOriginClient} — the GitHub-channel analogue of
|
|
9
6
|
* `createGitHubOctokit`.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
* `ORIGIN_APP_ID` (or
|
|
13
|
-
* `ORIGIN_APP_INSTALLATION_ID`,
|
|
14
|
-
* app's Ed25519 key; literal
|
|
15
|
-
* `ORIGIN_API_BASE_URL` for the public
|
|
8
|
+
* Self-configured channels with their own Origin app resolve credentials
|
|
9
|
+
* from options first, then the environment: `ORIGIN_APP_ID` (or
|
|
10
|
+
* `AGENT_SERVE_ORIGIN_APP_ID`), `ORIGIN_APP_INSTALLATION_ID`,
|
|
11
|
+
* `ORIGIN_APP_PRIVATE_KEY` (PKCS8 PEM for the app's Ed25519 key; literal
|
|
12
|
+
* `\n` sequences are normalized), and `ORIGIN_API_BASE_URL` for the public
|
|
13
|
+
* REST base.
|
|
14
|
+
*
|
|
15
|
+
* Hosted v2 forks configure none of that: their only credential is the
|
|
16
|
+
* deployment service account's `CURSOR_API_KEY`, exchanged for a Cursor
|
|
17
|
+
* session ({@link cursorSessionTokenProvider}, `POST
|
|
18
|
+
* /auth/exchange_user_api_key`) and sent as the Origin bearer. Reads and
|
|
19
|
+
* writes both run as the deployment **service-account principal**; Origin's
|
|
20
|
+
* `agent-serve` allowlist grants team-namespace read/write, so there is no
|
|
21
|
+
* app-installation token exchange for managed deployments.
|
|
16
22
|
*/
|
|
17
23
|
|
|
18
24
|
import { Buffer } from "node:buffer";
|
|
19
25
|
import { createPrivateKey, sign } from "node:crypto";
|
|
26
|
+
import {
|
|
27
|
+
cursorBackendUrl,
|
|
28
|
+
resolveApiKeySync,
|
|
29
|
+
} from "../../internal/cursor/credentials.js";
|
|
30
|
+
import {
|
|
31
|
+
CURSOR_SESSION_EXCHANGE_PATH,
|
|
32
|
+
exchangeCursorApiKeyForSession,
|
|
33
|
+
SESSION_TOKEN_EXPIRY_LEEWAY_MS,
|
|
34
|
+
} from "../../internal/cursor/session-token.js";
|
|
20
35
|
|
|
21
36
|
/**
|
|
22
37
|
* Public Origin REST base (check runs, installation tokens). Distinct from
|
|
@@ -58,7 +73,10 @@ export class OriginApiError extends Error {
|
|
|
58
73
|
}
|
|
59
74
|
}
|
|
60
75
|
|
|
61
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Yields a bearer for Origin REST calls (a short-lived `oit_…` for app
|
|
78
|
+
* channels, or a Cursor session token for service-account access).
|
|
79
|
+
*/
|
|
62
80
|
export type OriginTokenProvider = () => Promise<string>;
|
|
63
81
|
|
|
64
82
|
export interface OriginAppCredentials {
|
|
@@ -181,6 +199,147 @@ async function mintOriginInstallationToken(input: {
|
|
|
181
199
|
return token;
|
|
182
200
|
}
|
|
183
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Exchange endpoint for Cursor-account session tokens. Override with
|
|
204
|
+
* `AGENT_SERVE_CURSOR_SESSION_EXCHANGE_URL`; defaults to the Cursor backend.
|
|
205
|
+
*/
|
|
206
|
+
export function resolveCursorSessionExchangeUrl(): string {
|
|
207
|
+
const override = process.env.AGENT_SERVE_CURSOR_SESSION_EXCHANGE_URL;
|
|
208
|
+
if (override !== undefined && override.trim() !== "") {
|
|
209
|
+
return override.trim().replace(/\/+$/, "");
|
|
210
|
+
}
|
|
211
|
+
return `${cursorBackendUrl()}${CURSOR_SESSION_EXCHANGE_PATH}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A session-backed Origin token provider: {@link OriginTokenProvider} plus
|
|
216
|
+
* an `invalidate` hook `createOriginClient` calls when Origin rejects the
|
|
217
|
+
* bearer (401), so the next mint re-exchanges instead of serving the
|
|
218
|
+
* rejected session until its local renewal deadline.
|
|
219
|
+
*/
|
|
220
|
+
export type CursorSessionTokenProvider = OriginTokenProvider & {
|
|
221
|
+
invalidate: (token: string) => void;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Token provider backed by the shared Cursor session exchange
|
|
226
|
+
* (`exchangeCursorApiKeyForSession`, `POST /auth/exchange_user_api_key` —
|
|
227
|
+
* the same primitive `CursorBackendClient` mints through).
|
|
228
|
+
*
|
|
229
|
+
* Hosted v2 forks hold exactly one credential — the deployment service
|
|
230
|
+
* account's `CURSOR_API_KEY` — and Origin authenticates the exchanged
|
|
231
|
+
* session directly: reads and writes run as the deployment
|
|
232
|
+
* **service-account principal**, confined server-side to namespaces owned
|
|
233
|
+
* by the deployment's team (`agent-serve` allowlist). Local runs work the
|
|
234
|
+
* same way with a personal key: Origin authorizes the user principal.
|
|
235
|
+
*
|
|
236
|
+
* The API key is re-read on every mint so Bind can drop
|
|
237
|
+
* `CURSOR_API_KEY_FILE` onto a listening process; sessions are cached until
|
|
238
|
+
* shortly before their JWT expiry. The cache is **process-wide**, keyed by
|
|
239
|
+
* (endpoint, API key): hosted channels build a fresh `createOriginClient()`
|
|
240
|
+
* per event, so a per-provider cache would re-exchange on every read/write
|
|
241
|
+
* and concurrent first calls would race. In-flight exchanges are deduped
|
|
242
|
+
* for the same reason.
|
|
243
|
+
*/
|
|
244
|
+
const cursorSessionTokenCache = new Map<
|
|
245
|
+
string,
|
|
246
|
+
{ token: string; renewAtMs: number }
|
|
247
|
+
>();
|
|
248
|
+
const cursorSessionTokenInFlight = new Map<string, Promise<string>>();
|
|
249
|
+
|
|
250
|
+
/** Test-only: drop the process-wide exchanged-session cache. */
|
|
251
|
+
export function _clearCursorSessionTokenCacheForTesting(): void {
|
|
252
|
+
cursorSessionTokenCache.clear();
|
|
253
|
+
cursorSessionTokenInFlight.clear();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function cursorSessionTokenProvider(input?: {
|
|
257
|
+
/** Cursor API key source. Defaults to the standard host resolution. */
|
|
258
|
+
getApiKey?: () => string | undefined;
|
|
259
|
+
/** Exchange endpoint. Defaults to {@link resolveCursorSessionExchangeUrl}. */
|
|
260
|
+
exchangeUrl?: string;
|
|
261
|
+
fetch?: typeof fetch;
|
|
262
|
+
now?: () => number;
|
|
263
|
+
}): CursorSessionTokenProvider {
|
|
264
|
+
const getApiKey =
|
|
265
|
+
input?.getApiKey ?? ((): string | undefined => resolveApiKeySync()?.apiKey);
|
|
266
|
+
const fetchImpl = input?.fetch ?? fetch;
|
|
267
|
+
const now = input?.now ?? Date.now;
|
|
268
|
+
|
|
269
|
+
const cacheKey = (url: string, apiKey: string): string =>
|
|
270
|
+
`${url}\u0000${apiKey}`;
|
|
271
|
+
const resolveUrl = (): string =>
|
|
272
|
+
input?.exchangeUrl ?? resolveCursorSessionExchangeUrl();
|
|
273
|
+
|
|
274
|
+
const mint = async (url: string, apiKey: string): Promise<string> => {
|
|
275
|
+
const nowMs = now();
|
|
276
|
+
let session: { token: string; expiresAtMs: number };
|
|
277
|
+
try {
|
|
278
|
+
session = await exchangeCursorApiKeyForSession({
|
|
279
|
+
exchangeUrl: url,
|
|
280
|
+
apiKey,
|
|
281
|
+
fetchImpl,
|
|
282
|
+
nowMs,
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`originClient: ${
|
|
287
|
+
error instanceof Error ? error.message : String(error)
|
|
288
|
+
} Check CURSOR_API_KEY / CURSOR_API_KEY_FILE, or pass an explicit token.`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const renewAtMs = Math.max(
|
|
292
|
+
nowMs + 1_000,
|
|
293
|
+
session.expiresAtMs - SESSION_TOKEN_EXPIRY_LEEWAY_MS
|
|
294
|
+
);
|
|
295
|
+
cursorSessionTokenCache.set(cacheKey(url, apiKey), {
|
|
296
|
+
token: session.token,
|
|
297
|
+
renewAtMs,
|
|
298
|
+
});
|
|
299
|
+
return session.token;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const provider = async (): Promise<string> => {
|
|
303
|
+
const apiKey = getApiKey();
|
|
304
|
+
if (apiKey === undefined || apiKey === "") {
|
|
305
|
+
throw new Error(
|
|
306
|
+
"originClient: no Cursor credential available for the session exchange (set CURSOR_API_KEY or CURSOR_API_KEY_FILE)."
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
const url = resolveUrl();
|
|
310
|
+
const key = cacheKey(url, apiKey);
|
|
311
|
+
const cached = cursorSessionTokenCache.get(key);
|
|
312
|
+
if (cached !== undefined && now() < cached.renewAtMs) {
|
|
313
|
+
return cached.token;
|
|
314
|
+
}
|
|
315
|
+
// Dedupe concurrent first calls: one exchange per (endpoint, key).
|
|
316
|
+
const inFlight = cursorSessionTokenInFlight.get(key);
|
|
317
|
+
if (inFlight !== undefined) {
|
|
318
|
+
return await inFlight;
|
|
319
|
+
}
|
|
320
|
+
const pending = mint(url, apiKey).finally(() => {
|
|
321
|
+
cursorSessionTokenInFlight.delete(key);
|
|
322
|
+
});
|
|
323
|
+
cursorSessionTokenInFlight.set(key, pending);
|
|
324
|
+
return await pending;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// Origin answered 401 for this bearer: drop it (unless a newer session
|
|
328
|
+
// already replaced it) so the next call re-exchanges immediately.
|
|
329
|
+
const invalidate = (token: string): void => {
|
|
330
|
+
const apiKey = getApiKey();
|
|
331
|
+
if (apiKey === undefined || apiKey === "") {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const key = cacheKey(resolveUrl(), apiKey);
|
|
335
|
+
if (cursorSessionTokenCache.get(key)?.token === token) {
|
|
336
|
+
cursorSessionTokenCache.delete(key);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
return Object.assign(provider, { invalidate });
|
|
341
|
+
}
|
|
342
|
+
|
|
184
343
|
/**
|
|
185
344
|
* A cached per-use token provider for one set of app credentials. Every call
|
|
186
345
|
* inside the reuse window returns the same `oit_…`; the first call after it
|
|
@@ -224,6 +383,18 @@ export interface OriginClientOptions {
|
|
|
224
383
|
apiBaseUrl?: string;
|
|
225
384
|
/** Injectable fetch for tests. */
|
|
226
385
|
fetch?: typeof fetch;
|
|
386
|
+
/**
|
|
387
|
+
* Cursor-account session exchange fallback (hosted v2 deployment SAs and
|
|
388
|
+
* local personal keys). `true` (default) engages when no explicit token
|
|
389
|
+
* or app credentials resolve and a Cursor credential is present; `false`
|
|
390
|
+
* disables it; an object injects the key source / endpoint.
|
|
391
|
+
*/
|
|
392
|
+
cursorAccountExchange?:
|
|
393
|
+
| boolean
|
|
394
|
+
| {
|
|
395
|
+
getApiKey?: () => string | undefined;
|
|
396
|
+
exchangeUrl?: string;
|
|
397
|
+
};
|
|
227
398
|
}
|
|
228
399
|
|
|
229
400
|
/** Minimal authenticated Origin REST client (the `Octokit` analogue). */
|
|
@@ -242,8 +413,11 @@ export interface OriginClient {
|
|
|
242
413
|
|
|
243
414
|
/**
|
|
244
415
|
* Build an authenticated Origin REST client. Auth resolution: explicit
|
|
245
|
-
* token/provider, then app credentials (options or environment
|
|
246
|
-
*
|
|
416
|
+
* token/provider, then app credentials (options or environment, for
|
|
417
|
+
* channels with their own Origin app), then the Cursor-account session
|
|
418
|
+
* exchange (hosted v2 deployment SAs — reads and writes both run as the
|
|
419
|
+
* deployment service-account principal). Missing auth fails at the first
|
|
420
|
+
* request with a clear message.
|
|
247
421
|
*/
|
|
248
422
|
export function createOriginClient(
|
|
249
423
|
options: OriginClientOptions = {}
|
|
@@ -255,6 +429,10 @@ export function createOriginClient(
|
|
|
255
429
|
const fetchImpl = options.fetch ?? fetch;
|
|
256
430
|
|
|
257
431
|
let tokenProvider: OriginTokenProvider | undefined;
|
|
432
|
+
// Set when the session-exchange fallback is engaged: a 401 from Origin
|
|
433
|
+
// invalidates the cached session and retries once with a fresh one, the
|
|
434
|
+
// `CursorBackendClient` re-exchange behavior.
|
|
435
|
+
let sessionProvider: CursorSessionTokenProvider | undefined;
|
|
258
436
|
const getToken = async (): Promise<string> => {
|
|
259
437
|
if (typeof options.token === "string") {
|
|
260
438
|
return options.token;
|
|
@@ -264,20 +442,55 @@ export function createOriginClient(
|
|
|
264
442
|
}
|
|
265
443
|
if (tokenProvider === undefined) {
|
|
266
444
|
const credentials = options.credentials ?? resolveOriginAppCredentials();
|
|
267
|
-
|
|
445
|
+
const exchange = options.cursorAccountExchange ?? true;
|
|
446
|
+
const exchangeOptions =
|
|
447
|
+
exchange === false ? undefined : exchange === true ? {} : exchange;
|
|
448
|
+
const getExchangeApiKey =
|
|
449
|
+
exchangeOptions?.getApiKey ??
|
|
450
|
+
((): string | undefined => resolveApiKeySync()?.apiKey);
|
|
451
|
+
if (credentials !== undefined) {
|
|
452
|
+
tokenProvider = originAppTokenProvider({
|
|
453
|
+
credentials,
|
|
454
|
+
apiBaseUrl,
|
|
455
|
+
fetch: fetchImpl,
|
|
456
|
+
});
|
|
457
|
+
} else if (
|
|
458
|
+
exchangeOptions !== undefined &&
|
|
459
|
+
(getExchangeApiKey() ?? "") !== ""
|
|
460
|
+
) {
|
|
461
|
+
sessionProvider = cursorSessionTokenProvider({
|
|
462
|
+
fetch: fetchImpl,
|
|
463
|
+
getApiKey: getExchangeApiKey,
|
|
464
|
+
...(exchangeOptions.exchangeUrl === undefined
|
|
465
|
+
? {}
|
|
466
|
+
: { exchangeUrl: exchangeOptions.exchangeUrl }),
|
|
467
|
+
});
|
|
468
|
+
tokenProvider = sessionProvider;
|
|
469
|
+
} else {
|
|
268
470
|
throw new Error(
|
|
269
|
-
"originClient: no Origin credential available (pass token / credentials,
|
|
471
|
+
"originClient: no Origin credential available (pass token / credentials, set ORIGIN_APP_ID + ORIGIN_APP_INSTALLATION_ID + ORIGIN_APP_PRIVATE_KEY, or sign in with a Cursor credential for the session exchange)."
|
|
270
472
|
);
|
|
271
473
|
}
|
|
272
|
-
tokenProvider = originAppTokenProvider({
|
|
273
|
-
credentials,
|
|
274
|
-
apiBaseUrl,
|
|
275
|
-
fetch: fetchImpl,
|
|
276
|
-
});
|
|
277
474
|
}
|
|
278
475
|
return await tokenProvider();
|
|
279
476
|
};
|
|
280
477
|
|
|
478
|
+
const send = async (
|
|
479
|
+
input: { method: OriginApiMethod; path: string; body?: unknown },
|
|
480
|
+
path: string,
|
|
481
|
+
token: string
|
|
482
|
+
): Promise<Response> =>
|
|
483
|
+
await fetchImpl(`${apiBaseUrl}${path}`, {
|
|
484
|
+
method: input.method,
|
|
485
|
+
headers: {
|
|
486
|
+
authorization: `Bearer ${token}`,
|
|
487
|
+
...(input.body === undefined
|
|
488
|
+
? {}
|
|
489
|
+
: { "content-type": "application/json" }),
|
|
490
|
+
},
|
|
491
|
+
body: input.body === undefined ? undefined : JSON.stringify(input.body),
|
|
492
|
+
});
|
|
493
|
+
|
|
281
494
|
return {
|
|
282
495
|
apiBaseUrl,
|
|
283
496
|
async request<T = unknown>(input: {
|
|
@@ -287,16 +500,15 @@ export function createOriginClient(
|
|
|
287
500
|
}): Promise<T> {
|
|
288
501
|
const path = input.path.startsWith("/") ? input.path : `/${input.path}`;
|
|
289
502
|
const token = await getToken();
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
});
|
|
503
|
+
let response = await send(input, path, token);
|
|
504
|
+
if (response.status === 401 && sessionProvider !== undefined) {
|
|
505
|
+
// The session aged out server-side. Drop it — unless a concurrent
|
|
506
|
+
// retry already minted a newer one — and retry once.
|
|
507
|
+
await response.text().catch(() => undefined);
|
|
508
|
+
sessionProvider.invalidate(token);
|
|
509
|
+
const retryToken = await getToken();
|
|
510
|
+
response = await send(input, path, retryToken);
|
|
511
|
+
}
|
|
300
512
|
const text = await response.text();
|
|
301
513
|
let parsed: unknown;
|
|
302
514
|
try {
|
|
@@ -38,8 +38,8 @@ export interface OriginHandle {
|
|
|
38
38
|
}): Promise<T>;
|
|
39
39
|
/**
|
|
40
40
|
* Open a CI check run on `sha` and get a handle for updating and
|
|
41
|
-
* concluding it. Needs an app
|
|
42
|
-
*
|
|
41
|
+
* concluding it. Needs `repository:checks:write` — via an app
|
|
42
|
+
* installation, or the deployment service account on hosted v2 forks.
|
|
43
43
|
*/
|
|
44
44
|
createCheck(
|
|
45
45
|
input: Omit<OriginCheckCreateInput, "origin" | "owner" | "repo">
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
* fresh `externalUpdatedAt`, and `resumeOriginCheck` needs only those
|
|
8
8
|
* identity fields (no server id) to rebuild a handle after a restart.
|
|
9
9
|
*
|
|
10
|
-
* Posting needs `repository:checks:write
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `OriginApiError`.
|
|
10
|
+
* Posting needs `repository:checks:write`: an `oit_…` installation token for
|
|
11
|
+
* app channels, or a deployment service-account session (hosted v2 forks —
|
|
12
|
+
* the `agent-serve` allowlist grants checks:write on team-owned namespaces).
|
|
13
|
+
* See `createOriginClient` in `api.js`. Works with any client exposing
|
|
14
|
+
* `request()` (`ctx.origin`, `createOriginClient()`), and is also reachable
|
|
15
|
+
* as `ctx.origin.createCheck(...)`. API failures throw `OriginApiError`.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Origin identity is the signed-in Cursor user (no third-party OAuth). The
|
|
5
5
|
* channel declares repos at definition time; the serve host subscribes
|
|
6
|
-
* `/v0/scm-events` with `provider: "origin"` as that user.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* `/v0/scm-events` with `provider: "origin"` as that user. Outbound Origin
|
|
7
|
+
* API calls authenticate with the channel's app credentials when configured;
|
|
8
|
+
* hosted v2 deployments instead exchange the Cursor credential for a
|
|
9
|
+
* session (`cursorSessionTokenProvider` in ./api.ts), so reads and writes
|
|
10
|
+
* both run as the deployment service-account principal.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import type { OriginChannelOptions } from "./types.js";
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { deriveOriginWebhookEvents, ORIGIN_CHANNEL_KIND } from "./events.js";
|
|
27
27
|
import {
|
|
28
28
|
DEFAULT_ORIGIN_API_BASE_URL,
|
|
29
|
+
OriginJwksUnavailableError,
|
|
29
30
|
type OriginWebhookHeaders,
|
|
30
31
|
verifyOriginWebhook,
|
|
31
32
|
} from "./origin-webhook.js";
|
|
@@ -300,10 +301,12 @@ export function parseOriginPullRequestEvent(input: {
|
|
|
300
301
|
ctx: {
|
|
301
302
|
deliveryId: input.envelope.deliveryId,
|
|
302
303
|
eventType,
|
|
304
|
+
eventTime: input.envelope.event.eventTime,
|
|
303
305
|
installationId: input.envelope.installationId,
|
|
304
306
|
repository,
|
|
305
307
|
pullRequestNumber: number,
|
|
306
308
|
pullRequestUrl: url,
|
|
309
|
+
payload,
|
|
307
310
|
sender: originSender(input.envelope, payload),
|
|
308
311
|
},
|
|
309
312
|
pullRequest: {
|
|
@@ -687,7 +690,16 @@ export async function handleOriginWebhookRequest(input: {
|
|
|
687
690
|
await verifyOriginWebhook(bodyText, originHeaders(input.request), {
|
|
688
691
|
apiBaseUrl: resolveApiBaseUrl(input.options),
|
|
689
692
|
});
|
|
690
|
-
} catch {
|
|
693
|
+
} catch (error) {
|
|
694
|
+
// A JWKS outage means the delivery was never checked: answer 503 so
|
|
695
|
+
// Origin's delivery worker retries. Only a real signature mismatch
|
|
696
|
+
// is the terminal 401.
|
|
697
|
+
if (error instanceof OriginJwksUnavailableError) {
|
|
698
|
+
return Response.json(
|
|
699
|
+
{ ok: false, error: "signature_verification_unavailable" },
|
|
700
|
+
{ status: 503 }
|
|
701
|
+
);
|
|
702
|
+
}
|
|
691
703
|
return Response.json(
|
|
692
704
|
{ ok: false, error: "invalid_signature" },
|
|
693
705
|
{ status: 401 }
|
|
@@ -247,7 +247,31 @@ export interface VerifyOriginWebhookOptions {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
|
-
*
|
|
250
|
+
* The signing JWKS could not be fetched (and no cached copy applies), so
|
|
251
|
+
* the delivery was never actually checked. Callers must answer retryably
|
|
252
|
+
* (5xx) — a 401 would make Origin's delivery worker drop the wake as
|
|
253
|
+
* terminal on what is only a transient outage.
|
|
254
|
+
*/
|
|
255
|
+
export class OriginJwksUnavailableError extends Error {}
|
|
256
|
+
|
|
257
|
+
async function fetchJwksOrUnavailable(
|
|
258
|
+
apiBaseUrl: string,
|
|
259
|
+
fetchImpl: typeof fetch,
|
|
260
|
+
options?: FetchOriginJwksOptions
|
|
261
|
+
): Promise<readonly OriginJwk[]> {
|
|
262
|
+
try {
|
|
263
|
+
return await fetchOriginJwks(apiBaseUrl, fetchImpl, options);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
throw new OriginJwksUnavailableError(
|
|
266
|
+
`Origin JWKS unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Verify a Standard-Webhooks v1ed Origin payload. Throws
|
|
273
|
+
* {@link OriginJwksUnavailableError} when the keys cannot be fetched, and a
|
|
274
|
+
* plain error when the signature does not verify against them.
|
|
251
275
|
*/
|
|
252
276
|
export async function verifyOriginWebhook(
|
|
253
277
|
body: string | Buffer,
|
|
@@ -256,7 +280,7 @@ export async function verifyOriginWebhook(
|
|
|
256
280
|
): Promise<void> {
|
|
257
281
|
const apiBaseUrl = options?.apiBaseUrl ?? DEFAULT_ORIGIN_API_BASE_URL;
|
|
258
282
|
const fetchImpl = options?.fetchImpl ?? fetch;
|
|
259
|
-
const keys = await
|
|
283
|
+
const keys = await fetchJwksOrUnavailable(apiBaseUrl, fetchImpl);
|
|
260
284
|
if (
|
|
261
285
|
verifyV1edWebhookPayload({
|
|
262
286
|
publicJwks: keys,
|
|
@@ -267,7 +291,7 @@ export async function verifyOriginWebhook(
|
|
|
267
291
|
) {
|
|
268
292
|
return;
|
|
269
293
|
}
|
|
270
|
-
const refreshed = await
|
|
294
|
+
const refreshed = await fetchJwksOrUnavailable(apiBaseUrl, fetchImpl, {
|
|
271
295
|
force: true,
|
|
272
296
|
});
|
|
273
297
|
if (
|
|
@@ -51,11 +51,19 @@ export interface OriginReview {
|
|
|
51
51
|
export interface OriginEventContext {
|
|
52
52
|
deliveryId?: string;
|
|
53
53
|
eventType: string;
|
|
54
|
+
/** Delivery envelope `event.eventTime`, when present. */
|
|
55
|
+
eventTime?: string;
|
|
54
56
|
/** Origin app installation id from the delivery envelope, when present. */
|
|
55
57
|
installationId?: string;
|
|
56
58
|
repository: OriginRepositoryRef;
|
|
57
59
|
pullRequestNumber: number;
|
|
58
60
|
pullRequestUrl: string;
|
|
61
|
+
/**
|
|
62
|
+
* Raw parsed `event.payload` record (`{}` when absent), for consumers
|
|
63
|
+
* whose normalization needs more than the typed event — the GitHub
|
|
64
|
+
* channel's hook ctx exposes the same.
|
|
65
|
+
*/
|
|
66
|
+
payload: Record<string, unknown>;
|
|
59
67
|
sender?: OriginActor;
|
|
60
68
|
host: HostContext;
|
|
61
69
|
artifacts: ArtifactsApi;
|
|
@@ -193,6 +193,12 @@ export function truncateTitle(title: string, max?: number): string {
|
|
|
193
193
|
// Assistant / stream helpers
|
|
194
194
|
// ---------------------------------------------------------------------------
|
|
195
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Show or clear the Working chip. Agent UX ignores empty
|
|
198
|
+
* `assistant.threads.setStatus`; `agents.sessions.setStatus` `active` is
|
|
199
|
+
* what actually clears it. The assistant call stays for loading_messages
|
|
200
|
+
* and older workspaces.
|
|
201
|
+
*/
|
|
196
202
|
export async function setThreadStatus(args: {
|
|
197
203
|
botToken: SlackBotToken | undefined;
|
|
198
204
|
channelId: string;
|
|
@@ -204,13 +210,21 @@ export async function setThreadStatus(args: {
|
|
|
204
210
|
return { ok: false, error: "missing_channel_or_thread" };
|
|
205
211
|
}
|
|
206
212
|
const status = truncateTypingStatus(args.status);
|
|
213
|
+
const sessionResult = await callSlackApiSoft({
|
|
214
|
+
botToken: args.botToken,
|
|
215
|
+
operation: "agents.sessions.setStatus",
|
|
216
|
+
body: {
|
|
217
|
+
channel_id: args.channelId,
|
|
218
|
+
thread_ts: args.threadTs,
|
|
219
|
+
status: status.length > 0 ? "processing" : "active",
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
207
223
|
const body: Record<string, unknown> = {
|
|
208
224
|
channel_id: args.channelId,
|
|
209
225
|
thread_ts: args.threadTs,
|
|
210
226
|
status,
|
|
211
227
|
};
|
|
212
|
-
// Empty status clears Slack's assistant chip ("App is working…").
|
|
213
|
-
// Sending loading_messages with it keeps the chip rotating.
|
|
214
228
|
if (status.length > 0) {
|
|
215
229
|
const loadingRaw =
|
|
216
230
|
args.loadingMessages !== undefined && args.loadingMessages.length > 0
|
|
@@ -224,11 +238,19 @@ export async function setThreadStatus(args: {
|
|
|
224
238
|
body.loading_messages = loading_messages;
|
|
225
239
|
}
|
|
226
240
|
}
|
|
227
|
-
|
|
241
|
+
const assistantResult = await callSlackApiSoft({
|
|
228
242
|
botToken: args.botToken,
|
|
229
243
|
operation: "assistant.threads.setStatus",
|
|
230
244
|
body,
|
|
231
245
|
});
|
|
246
|
+
|
|
247
|
+
if (sessionResult.ok) {
|
|
248
|
+
return sessionResult;
|
|
249
|
+
}
|
|
250
|
+
if (sessionResult.unsupported === true || assistantResult.ok) {
|
|
251
|
+
return assistantResult;
|
|
252
|
+
}
|
|
253
|
+
return sessionResult;
|
|
232
254
|
}
|
|
233
255
|
|
|
234
256
|
export async function setThreadTitle(args: {
|
|
@@ -278,16 +300,30 @@ export async function startChatStream(args: {
|
|
|
278
300
|
channel: string;
|
|
279
301
|
thread_ts?: string;
|
|
280
302
|
markdown_text?: string;
|
|
303
|
+
chunks?: StreamTaskChunk[];
|
|
304
|
+
recipient_user_id?: string;
|
|
305
|
+
recipient_team_id?: string;
|
|
281
306
|
}): Promise<SoftSlackResult<{ ts: string; channel: string }>> {
|
|
282
307
|
const body: Record<string, unknown> = {
|
|
283
308
|
channel: args.channel,
|
|
284
309
|
};
|
|
310
|
+
// Empty markdown_text is rejected or opens a stream the assistant pane
|
|
311
|
+
// never attaches. Recipients are required outside DMs.
|
|
285
312
|
if (args.thread_ts !== undefined && args.thread_ts !== "") {
|
|
286
313
|
body.thread_ts = args.thread_ts;
|
|
287
314
|
}
|
|
288
|
-
if (args.markdown_text !== undefined) {
|
|
315
|
+
if (args.markdown_text !== undefined && args.markdown_text !== "") {
|
|
289
316
|
body.markdown_text = args.markdown_text;
|
|
290
317
|
}
|
|
318
|
+
if (args.chunks !== undefined && args.chunks.length > 0) {
|
|
319
|
+
body.chunks = args.chunks;
|
|
320
|
+
}
|
|
321
|
+
if (args.recipient_user_id !== undefined && args.recipient_user_id !== "") {
|
|
322
|
+
body.recipient_user_id = args.recipient_user_id;
|
|
323
|
+
}
|
|
324
|
+
if (args.recipient_team_id !== undefined && args.recipient_team_id !== "") {
|
|
325
|
+
body.recipient_team_id = args.recipient_team_id;
|
|
326
|
+
}
|
|
291
327
|
const result = await callSlackApiSoft({
|
|
292
328
|
botToken: args.botToken,
|
|
293
329
|
operation: "chat.startStream",
|
|
@@ -642,7 +678,7 @@ export function buildSlackBinding(args: {
|
|
|
642
678
|
...(loadingMessages === undefined ? {} : { loadingMessages }),
|
|
643
679
|
});
|
|
644
680
|
if (!result.ok) {
|
|
645
|
-
log.warn("
|
|
681
|
+
log.warn("slack setStatus not-ok", {
|
|
646
682
|
error: result.error,
|
|
647
683
|
channel: args.channelId,
|
|
648
684
|
thread_ts: threadTs,
|