@cursor/july 0.1.74 → 0.1.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channels/checks.d.ts +111 -0
- package/dist/channels/checks.d.ts.map +1 -0
- package/dist/channels/checks.js +86 -0
- package/dist/channels/deployments/deployments-channel.d.ts +44 -0
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -0
- package/dist/channels/deployments/deployments-channel.js +92 -0
- package/dist/channels/deployments/index.d.ts +26 -0
- package/dist/channels/deployments/index.d.ts.map +1 -0
- package/dist/channels/deployments/index.js +25 -0
- package/dist/channels/deployments/types.d.ts +69 -0
- package/dist/channels/deployments/types.d.ts.map +1 -0
- package/dist/channels/deployments/types.js +6 -0
- package/dist/channels/github/checks.d.ts +13 -48
- package/dist/channels/github/checks.d.ts.map +1 -1
- package/dist/channels/github/checks.js +56 -64
- package/dist/channels/origin/api.d.ts +100 -0
- package/dist/channels/origin/api.d.ts.map +1 -0
- package/dist/channels/origin/api.js +225 -0
- package/dist/channels/origin/binding.d.ts +44 -0
- package/dist/channels/origin/binding.d.ts.map +1 -0
- package/dist/channels/origin/binding.js +45 -0
- package/dist/channels/origin/checks.d.ts +107 -0
- package/dist/channels/origin/checks.d.ts.map +1 -0
- package/dist/channels/origin/checks.js +145 -0
- package/dist/channels/origin/index.d.ts +3 -0
- package/dist/channels/origin/index.d.ts.map +1 -1
- package/dist/channels/origin/index.js +3 -0
- package/dist/channels/origin/origin-channel.d.ts +4 -4
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +12 -2
- package/dist/channels/origin/types.d.ts +36 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels.d.ts +1 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +1 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.BETd-WqU.js → app.b8bhB2SV.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DGZeXVqn.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.wEaGEmV9.js → VPLocalSearchBox.lLSeIyWw.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DNDicFOP.js → theme.C_owesa-.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.MN8iDy8V.js → reference_channels.md.G5GysZ7e.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.MN8iDy8V.lean.js → reference_channels.md.G5GysZ7e.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/convert-automation.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +5 -5
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/templates/agentic-owners.html +3 -3
- package/dist/docs/templates/demo.html +3 -3
- package/dist/docs/templates/pr-autofixer.html +3 -3
- package/dist/docs/templates/security-reviewer.html +3 -3
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/internal/authored-alias-hooks.d.ts +12 -0
- package/dist/internal/authored-alias-hooks.d.ts.map +1 -1
- package/dist/internal/authored-alias-hooks.js +60 -8
- package/dist/internal/authored-loaders.d.ts +5 -3
- package/dist/internal/authored-loaders.d.ts.map +1 -1
- package/dist/internal/authored-loaders.js +13 -4
- package/dist/internal/cursor-deployment-relay.d.ts +77 -0
- package/dist/internal/cursor-deployment-relay.d.ts.map +1 -0
- package/dist/internal/cursor-deployment-relay.js +201 -0
- package/dist/internal/deployment-scm-event-stream.js +4 -4
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/review-comments.d.ts +62 -0
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +199 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +114 -5
- package/dist/playground/assets/{index-D_8I9TM0.js → index-BWqjJcRo.js} +1 -1
- package/dist/playground/assets/index-Bfwz2yLN.css +1 -0
- package/dist/playground/index.html +2 -2
- package/docs/reference/channels.md +15 -0
- package/package.json +7 -1
- package/src/channels/checks.ts +185 -0
- package/src/channels/deployments/deployments-channel.ts +109 -0
- package/src/channels/deployments/index.ts +26 -0
- package/src/channels/deployments/types.ts +80 -0
- package/src/channels/github/checks.ts +90 -115
- package/src/channels/origin/api.ts +318 -0
- package/src/channels/origin/binding.ts +96 -0
- package/src/channels/origin/checks.ts +250 -0
- package/src/channels/origin/index.ts +3 -0
- package/src/channels/origin/origin-channel.ts +18 -6
- package/src/channels/origin/types.ts +37 -0
- package/src/channels.ts +16 -0
- package/src/internal/authored-alias-hooks.ts +75 -7
- package/src/internal/authored-loaders.ts +15 -4
- package/src/internal/cursor-deployment-relay.ts +274 -0
- package/src/internal/deployment-scm-event-stream.ts +4 -4
- package/src/internal/distribution.ts +1 -0
- package/src/internal/review-comments.ts +286 -0
- package/src/internal/server.ts +149 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.DM5INS71.js +0 -1
- package/dist/playground/assets/index-M3nSMc6_.css +0 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Origin CI checks — the Origin {@link CiCheckWriter} behind the shared check
|
|
3
|
+
* lifecycle in `../checks.js`.
|
|
4
|
+
*
|
|
5
|
+
* Origin check runs are idempotent upserts keyed by `(repo, head SHA, suite
|
|
6
|
+
* key, check key)`, so every write re-POSTs the full desired state with a
|
|
7
|
+
* fresh `externalUpdatedAt`, and `resumeOriginCheck` needs only those
|
|
8
|
+
* identity fields (no server id) to rebuild a handle after a restart.
|
|
9
|
+
*
|
|
10
|
+
* Posting needs `repository:checks:write` and is installation-only: the
|
|
11
|
+
* client must authenticate with an `oit_…` installation token (see
|
|
12
|
+
* `createOriginClient` / `originAppTokenProvider` in `api.js`). Works with
|
|
13
|
+
* any client exposing `request()` (`ctx.origin`, `createOriginClient()`),
|
|
14
|
+
* and is also reachable as `ctx.origin.createCheck(...)`. API failures throw
|
|
15
|
+
* `OriginApiError`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
type CiCheck,
|
|
20
|
+
type CiCheckConcludeInput,
|
|
21
|
+
type CiCheckConclusion,
|
|
22
|
+
type CiCheckCreateInput,
|
|
23
|
+
type CiCheckOutput,
|
|
24
|
+
type CiCheckRef,
|
|
25
|
+
type CiCheckStatus,
|
|
26
|
+
type CiChecksClient,
|
|
27
|
+
type CiCheckUpdateInput,
|
|
28
|
+
type CiCheckWriter,
|
|
29
|
+
openCiCheck,
|
|
30
|
+
resumeCiCheck,
|
|
31
|
+
} from "../checks.js";
|
|
32
|
+
import type { OriginApiMethod } from "./api.js";
|
|
33
|
+
|
|
34
|
+
/** Any client that can call the Origin public REST API. */
|
|
35
|
+
export interface OriginCheckClient {
|
|
36
|
+
request<T = unknown>(input: {
|
|
37
|
+
method: OriginApiMethod;
|
|
38
|
+
path: string;
|
|
39
|
+
body?: unknown;
|
|
40
|
+
}): Promise<T>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type OriginCheckRunStatus = CiCheckStatus;
|
|
44
|
+
|
|
45
|
+
/** Origin accepts the shared conclusion set plus `stale`. */
|
|
46
|
+
export type OriginCheckRunConclusion = CiCheckConclusion | "stale";
|
|
47
|
+
|
|
48
|
+
export type OriginCheckOutput = CiCheckOutput;
|
|
49
|
+
export type OriginCheckUpdateInput =
|
|
50
|
+
CiCheckUpdateInput<OriginCheckRunConclusion>;
|
|
51
|
+
export type OriginCheckConcludeInput =
|
|
52
|
+
CiCheckConcludeInput<OriginCheckRunConclusion>;
|
|
53
|
+
|
|
54
|
+
/** A created check run. All methods throw `OriginApiError` on API failure. */
|
|
55
|
+
export type OriginCheck = CiCheck<OriginCheckRunConclusion> & CiCheckRef;
|
|
56
|
+
|
|
57
|
+
/** The upsert identity + client an Origin check is bound to. */
|
|
58
|
+
export interface OriginCheckIdentityInput {
|
|
59
|
+
/** Client used for every API call on the returned handle. */
|
|
60
|
+
origin: OriginCheckClient;
|
|
61
|
+
/** Repository owner slug, e.g. `"anysphere"`. */
|
|
62
|
+
owner: string;
|
|
63
|
+
/** Repository name, e.g. `"everysphere"`. */
|
|
64
|
+
repo: string;
|
|
65
|
+
/** Check name shown in the PR merge box, e.g. `"Cursor Security Review"`. */
|
|
66
|
+
name: string;
|
|
67
|
+
/** Commit the check attaches to (usually the PR head SHA). */
|
|
68
|
+
sha: string;
|
|
69
|
+
/**
|
|
70
|
+
* Stable, app-chosen key identifying the logical check across attempts
|
|
71
|
+
* (part of the upsert identity). Defaults to a slug of `name`.
|
|
72
|
+
*/
|
|
73
|
+
key?: string;
|
|
74
|
+
/** Suite the check belongs to. Defaults to a suite mirroring the check. */
|
|
75
|
+
suite?: { key?: string; name?: string; detailsUrl?: string };
|
|
76
|
+
/**
|
|
77
|
+
* Provider-assigned immutable identity for this check attempt. Defaults to
|
|
78
|
+
* a deterministic id derived from the keys and `sha`.
|
|
79
|
+
*/
|
|
80
|
+
externalId?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface OriginCheckCreateInput
|
|
84
|
+
extends OriginCheckIdentityInput,
|
|
85
|
+
CiCheckCreateInput {}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Origin rejects the whole upsert when `output.title` exceeds 255 characters
|
|
89
|
+
* or a body exceeds 65,535 UTF-8 bytes, so the writer caps rather than
|
|
90
|
+
* risking a lost check write.
|
|
91
|
+
*/
|
|
92
|
+
const OUTPUT_TITLE_MAX_CHARS = 255;
|
|
93
|
+
const OUTPUT_BODY_MAX_BYTES = 65_000;
|
|
94
|
+
|
|
95
|
+
/** Truncate to at most `maxBytes` UTF-8 bytes without severing a character. */
|
|
96
|
+
function capUtf8Bytes(text: string, maxBytes: number): string {
|
|
97
|
+
const encoded = new TextEncoder().encode(text);
|
|
98
|
+
if (encoded.length <= maxBytes) {
|
|
99
|
+
return text;
|
|
100
|
+
}
|
|
101
|
+
// A non-fatal decode turns a trailing partial character into U+FFFD;
|
|
102
|
+
// stripping it keeps the result valid (and re-encodable) under the cap.
|
|
103
|
+
return new TextDecoder("utf-8")
|
|
104
|
+
.decode(encoded.subarray(0, maxBytes))
|
|
105
|
+
.replace(/\uFFFD+$/, "");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function cappedOutput(
|
|
109
|
+
output: CiCheckOutput | undefined
|
|
110
|
+
): Record<string, unknown> | undefined {
|
|
111
|
+
if (output === undefined) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
title: output.title.slice(0, OUTPUT_TITLE_MAX_CHARS),
|
|
116
|
+
summary: capUtf8Bytes(output.summary, OUTPUT_BODY_MAX_BYTES),
|
|
117
|
+
...(output.text === undefined
|
|
118
|
+
? {}
|
|
119
|
+
: { text: capUtf8Bytes(output.text, OUTPUT_BODY_MAX_BYTES) }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Derive a stable check key from a display name, e.g. `cursor-security-review`. */
|
|
124
|
+
export function originCheckKeyFromName(name: string): string {
|
|
125
|
+
const slug = name
|
|
126
|
+
.toLowerCase()
|
|
127
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
128
|
+
.replace(/^-+|-+$/g, "")
|
|
129
|
+
.slice(0, 100);
|
|
130
|
+
return slug === "" ? "check" : slug;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Origin writer: every write is a full keyed upsert. Tracks the last written
|
|
135
|
+
* status so an output-only update cannot regress a running check to queued.
|
|
136
|
+
*/
|
|
137
|
+
function originCheckWriter(
|
|
138
|
+
input: OriginCheckIdentityInput & { status?: CiCheckStatus }
|
|
139
|
+
): CiCheckWriter<OriginCheckRunConclusion> {
|
|
140
|
+
const key = input.key ?? originCheckKeyFromName(input.name);
|
|
141
|
+
const suiteKey = input.suite?.key ?? key;
|
|
142
|
+
const externalId =
|
|
143
|
+
input.externalId ?? `agent-serve:${suiteKey}:${key}:${input.sha}`;
|
|
144
|
+
const path = `/v1/origin/repos/${encodeURIComponent(input.owner)}/${encodeURIComponent(
|
|
145
|
+
input.repo
|
|
146
|
+
)}/check-runs`;
|
|
147
|
+
let lastStatus: CiCheckStatus = input.status ?? "in_progress";
|
|
148
|
+
return async (write) => {
|
|
149
|
+
const status =
|
|
150
|
+
write.status ??
|
|
151
|
+
(write.conclusion === undefined ? lastStatus : "completed");
|
|
152
|
+
lastStatus = status;
|
|
153
|
+
const response = await input.origin.request<{
|
|
154
|
+
checkRun?: { id?: unknown };
|
|
155
|
+
}>({
|
|
156
|
+
method: "POST",
|
|
157
|
+
path,
|
|
158
|
+
body: {
|
|
159
|
+
headSha: input.sha,
|
|
160
|
+
checkSuite: {
|
|
161
|
+
key: suiteKey,
|
|
162
|
+
name: input.suite?.name ?? input.name,
|
|
163
|
+
externalId: `agent-serve:${suiteKey}`,
|
|
164
|
+
...(input.suite?.detailsUrl === undefined
|
|
165
|
+
? {}
|
|
166
|
+
: { detailsUrl: input.suite.detailsUrl }),
|
|
167
|
+
},
|
|
168
|
+
checkRun: {
|
|
169
|
+
key,
|
|
170
|
+
name: input.name,
|
|
171
|
+
status,
|
|
172
|
+
conclusion: write.conclusion,
|
|
173
|
+
// Origin only applies an upsert when this timestamp is at least as
|
|
174
|
+
// new as the stored check; timestamp every write when it is built.
|
|
175
|
+
externalUpdatedAt: new Date().toISOString(),
|
|
176
|
+
externalId,
|
|
177
|
+
startedAt: write.startedAt,
|
|
178
|
+
completedAt: write.completedAt,
|
|
179
|
+
detailsUrl: write.detailsUrl,
|
|
180
|
+
output: cappedOutput(write.output),
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
const id = response?.checkRun?.id;
|
|
185
|
+
return typeof id === "string" ? { id } : undefined;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Create a CI check run on a commit and return a handle for later updates.
|
|
191
|
+
*
|
|
192
|
+
* ```ts
|
|
193
|
+
* const check = await createOriginCheck({
|
|
194
|
+
* origin: ctx.origin,
|
|
195
|
+
* owner: ctx.repository.owner,
|
|
196
|
+
* repo: ctx.repository.name,
|
|
197
|
+
* name: "Cursor Security Review",
|
|
198
|
+
* sha: pr.headSha,
|
|
199
|
+
* detailsUrl: sessionUrl,
|
|
200
|
+
* output: { title: "Running…", summary: `[View session](${sessionUrl})` },
|
|
201
|
+
* });
|
|
202
|
+
* // …do the work…
|
|
203
|
+
* await check.conclude({ conclusion: "success", output: { … } });
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
export function createOriginCheck(
|
|
207
|
+
input: OriginCheckCreateInput
|
|
208
|
+
): Promise<OriginCheck> {
|
|
209
|
+
return openCiCheck(originCheckWriter(input), input);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Rebuild an {@link OriginCheck} handle for an existing check run (e.g.
|
|
214
|
+
* after a process restart) from its upsert identity — no server id needed.
|
|
215
|
+
* No API call is made until the first update.
|
|
216
|
+
*/
|
|
217
|
+
export function resumeOriginCheck(
|
|
218
|
+
input: OriginCheckIdentityInput & {
|
|
219
|
+
/** Last known status; defaults to `"in_progress"`. */
|
|
220
|
+
status?: CiCheckStatus;
|
|
221
|
+
/** Server-assigned id (`cr_…`) when known. */
|
|
222
|
+
id?: string;
|
|
223
|
+
}
|
|
224
|
+
): OriginCheck {
|
|
225
|
+
return resumeCiCheck(
|
|
226
|
+
originCheckWriter(input),
|
|
227
|
+
input.id === undefined ? undefined : { id: input.id }
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Repo-bound checks adapter satisfying the shared {@link CiChecksClient}
|
|
233
|
+
* contract — hand it to forge-agnostic check code when there is no channel
|
|
234
|
+
* `ctx.origin` (e.g. CI entrypoints).
|
|
235
|
+
*/
|
|
236
|
+
export function bindOriginChecks(input: {
|
|
237
|
+
origin: OriginCheckClient;
|
|
238
|
+
owner: string;
|
|
239
|
+
repo: string;
|
|
240
|
+
}): CiChecksClient<OriginCheckRunConclusion> {
|
|
241
|
+
return {
|
|
242
|
+
createCheck: (check) =>
|
|
243
|
+
createOriginCheck({
|
|
244
|
+
...check,
|
|
245
|
+
origin: input.origin,
|
|
246
|
+
owner: input.owner,
|
|
247
|
+
repo: input.repo,
|
|
248
|
+
}),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* matching GitHub's `github-webhook` pattern.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
export * from "./api.js";
|
|
26
|
+
export * from "./binding.js";
|
|
27
|
+
export * from "./checks.js";
|
|
25
28
|
export * from "./cursor-account.js";
|
|
26
29
|
export * from "./events.js";
|
|
27
30
|
export * from "./origin-channel.js";
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ChannelDefinition,
|
|
14
14
|
ChannelHandlerArgs,
|
|
15
15
|
} from "../../types.js";
|
|
16
|
+
import { buildOriginBinding } from "./binding.js";
|
|
16
17
|
import {
|
|
17
18
|
parseCursorOriginRepoScope,
|
|
18
19
|
registerOriginCursorAccountRuntime,
|
|
@@ -268,7 +269,7 @@ function originSender(
|
|
|
268
269
|
export function parseOriginPullRequestEvent(input: {
|
|
269
270
|
envelope: OriginWebhookEnvelope;
|
|
270
271
|
}): {
|
|
271
|
-
ctx: Omit<OriginEventContext, "host" | "artifacts">;
|
|
272
|
+
ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">;
|
|
272
273
|
pullRequest: OriginPullRequestEvent;
|
|
273
274
|
payload: Record<string, unknown>;
|
|
274
275
|
} | null {
|
|
@@ -294,6 +295,7 @@ export function parseOriginPullRequestEvent(input: {
|
|
|
294
295
|
ctx: {
|
|
295
296
|
deliveryId: input.envelope.deliveryId,
|
|
296
297
|
eventType,
|
|
298
|
+
installationId: input.envelope.installationId,
|
|
297
299
|
repository,
|
|
298
300
|
pullRequestNumber: number,
|
|
299
301
|
pullRequestUrl: url,
|
|
@@ -332,7 +334,7 @@ export function parseOriginPullRequestEvent(input: {
|
|
|
332
334
|
* as {@link defaultGitHubAuth}).
|
|
333
335
|
*/
|
|
334
336
|
export function defaultOriginAuth(
|
|
335
|
-
ctx: Omit<OriginEventContext, "host" | "artifacts">
|
|
337
|
+
ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">
|
|
336
338
|
): AuthContext {
|
|
337
339
|
const senderId = ctx.sender?.id;
|
|
338
340
|
return {
|
|
@@ -363,7 +365,7 @@ export function defaultOriginTitle(
|
|
|
363
365
|
}
|
|
364
366
|
|
|
365
367
|
export function defaultOriginPullRequestMessage(
|
|
366
|
-
ctx: Omit<OriginEventContext, "host" | "artifacts">,
|
|
368
|
+
ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">,
|
|
367
369
|
pullRequest: OriginPullRequestEvent
|
|
368
370
|
): string {
|
|
369
371
|
const head =
|
|
@@ -381,7 +383,7 @@ export function defaultOriginPullRequestMessage(
|
|
|
381
383
|
}
|
|
382
384
|
|
|
383
385
|
export function defaultOriginPullRequestDispatch(
|
|
384
|
-
ctx: Omit<OriginEventContext, "host" | "artifacts">,
|
|
386
|
+
ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">,
|
|
385
387
|
pullRequest: OriginPullRequestEvent
|
|
386
388
|
): OriginTurnDispatch | null {
|
|
387
389
|
if (!DEFAULT_PULL_REQUEST_TYPES.has(pullRequest.type)) {
|
|
@@ -397,13 +399,19 @@ function isTaskDispatch(
|
|
|
397
399
|
}
|
|
398
400
|
|
|
399
401
|
function enrichEventContext(input: {
|
|
400
|
-
ctx: Omit<OriginEventContext, "host" | "artifacts">;
|
|
402
|
+
ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">;
|
|
401
403
|
args: ChannelHandlerArgs;
|
|
404
|
+
options: OriginChannelOptions;
|
|
402
405
|
}): OriginEventContext {
|
|
403
406
|
return {
|
|
404
407
|
...input.ctx,
|
|
405
408
|
host: input.args.host,
|
|
406
409
|
artifacts: input.args.artifacts,
|
|
410
|
+
origin: buildOriginBinding({
|
|
411
|
+
repository: input.ctx.repository,
|
|
412
|
+
installationId: input.ctx.installationId,
|
|
413
|
+
app: input.options.app,
|
|
414
|
+
}),
|
|
407
415
|
};
|
|
408
416
|
}
|
|
409
417
|
|
|
@@ -525,7 +533,11 @@ export async function dispatchOriginWebhook(input: {
|
|
|
525
533
|
{ status: 400 }
|
|
526
534
|
);
|
|
527
535
|
}
|
|
528
|
-
const ctx = enrichEventContext({
|
|
536
|
+
const ctx = enrichEventContext({
|
|
537
|
+
ctx: parsed.ctx,
|
|
538
|
+
args: input.args,
|
|
539
|
+
options: input.options,
|
|
540
|
+
});
|
|
529
541
|
const { pullRequest, payload } = parsed;
|
|
530
542
|
const repoAllowlist = configuredOriginRepoAllowlist(input.options);
|
|
531
543
|
if (
|
|
@@ -6,6 +6,8 @@ import type {
|
|
|
6
6
|
ChannelStartArgs,
|
|
7
7
|
HostContext,
|
|
8
8
|
} from "../../types.js";
|
|
9
|
+
import type { OriginTokenProvider } from "./api.js";
|
|
10
|
+
import type { OriginHandle } from "./binding.js";
|
|
9
11
|
|
|
10
12
|
export interface OriginActor {
|
|
11
13
|
id: string;
|
|
@@ -48,12 +50,21 @@ export interface OriginReview {
|
|
|
48
50
|
export interface OriginEventContext {
|
|
49
51
|
deliveryId?: string;
|
|
50
52
|
eventType: string;
|
|
53
|
+
/** Origin app installation id from the delivery envelope, when present. */
|
|
54
|
+
installationId?: string;
|
|
51
55
|
repository: OriginRepositoryRef;
|
|
52
56
|
pullRequestNumber: number;
|
|
53
57
|
pullRequestUrl: string;
|
|
54
58
|
sender?: OriginActor;
|
|
55
59
|
host: HostContext;
|
|
56
60
|
artifacts: ArtifactsApi;
|
|
61
|
+
/**
|
|
62
|
+
* Repo-bound Origin public API handle (REST calls + `createCheck`).
|
|
63
|
+
* Outbound credentials come from {@link OriginChannelOptions.app} or the
|
|
64
|
+
* `ORIGIN_APP_*` environment; resolution is lazy, so hooks that never call
|
|
65
|
+
* it need no credentials.
|
|
66
|
+
*/
|
|
67
|
+
origin: OriginHandle;
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
export type OriginTurnDispatch = {
|
|
@@ -114,6 +125,26 @@ export interface OriginWebhookEnvelope {
|
|
|
114
125
|
};
|
|
115
126
|
}
|
|
116
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Outbound Origin app credentials for `ctx.origin` (public REST calls, CI
|
|
130
|
+
* check runs). Fields left unset fall back to the environment
|
|
131
|
+
* (`ORIGIN_APP_ID` / `ORIGIN_APP_INSTALLATION_ID` / `ORIGIN_APP_PRIVATE_KEY`
|
|
132
|
+
* / `ORIGIN_API_BASE_URL`); the delivery's installation id fills in when
|
|
133
|
+
* neither pins one.
|
|
134
|
+
*/
|
|
135
|
+
export interface OriginAppOptions {
|
|
136
|
+
/** Origin app id (`app_<typeid>`). */
|
|
137
|
+
appId?: string;
|
|
138
|
+
/** Installation id outbound tokens are minted for. */
|
|
139
|
+
installationId?: string;
|
|
140
|
+
/** PKCS8 PEM for the app's EdDSA (Ed25519) private key. */
|
|
141
|
+
privateKey?: string;
|
|
142
|
+
/** Static bearer (`oit_…`) or provider, bypassing the app-JWT mint. */
|
|
143
|
+
token?: string | OriginTokenProvider;
|
|
144
|
+
/** Public Origin REST base, e.g. `https://api.cursor.com`. */
|
|
145
|
+
apiBaseUrl?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
117
148
|
export interface OriginChannelOptions {
|
|
118
149
|
/**
|
|
119
150
|
* Use the signed-in Cursor account for inbound Origin events via
|
|
@@ -149,6 +180,12 @@ export interface OriginChannelOptions {
|
|
|
149
180
|
* (GitHub's no-secret posture); `--dev` / skip-verify admits loopback only.
|
|
150
181
|
*/
|
|
151
182
|
auth?: AuthPolicy[];
|
|
183
|
+
/**
|
|
184
|
+
* Outbound Origin app credentials for `ctx.origin` (REST calls, CI check
|
|
185
|
+
* runs). Optional: every field falls back to the `ORIGIN_APP_*`
|
|
186
|
+
* environment, and inbound-only channels need none.
|
|
187
|
+
*/
|
|
188
|
+
app?: OriginAppOptions;
|
|
152
189
|
/** Route path under `/v1/channels/<name>`. Defaults to `/`. */
|
|
153
190
|
path?: string;
|
|
154
191
|
/** Origin API base used to fetch `/v1/origin/keys` for v1ed verify. */
|
package/src/channels.ts
CHANGED
|
@@ -47,6 +47,22 @@
|
|
|
47
47
|
* Handlers receive `args.body` / `args.query` inferred from those schemas.
|
|
48
48
|
*/
|
|
49
49
|
|
|
50
|
+
export {
|
|
51
|
+
type CiCheck,
|
|
52
|
+
type CiCheckConcludeInput,
|
|
53
|
+
type CiCheckConclusion,
|
|
54
|
+
type CiCheckCreateInput,
|
|
55
|
+
type CiCheckOutput,
|
|
56
|
+
type CiCheckRef,
|
|
57
|
+
type CiCheckStatus,
|
|
58
|
+
type CiChecksClient,
|
|
59
|
+
type CiCheckUpdateInput,
|
|
60
|
+
type CiCheckWrite,
|
|
61
|
+
type CiCheckWriter,
|
|
62
|
+
openCiCheck,
|
|
63
|
+
resumeCiCheck,
|
|
64
|
+
} from "./channels/checks.js";
|
|
65
|
+
|
|
50
66
|
import { Buffer } from "node:buffer";
|
|
51
67
|
import { timingSafeEqual } from "node:crypto";
|
|
52
68
|
import { brandDefinition } from "./internal/brand.js";
|
|
@@ -14,16 +14,29 @@
|
|
|
14
14
|
* project's own node_modules; `fallbackAliases` only apply when that
|
|
15
15
|
* fails (npx/global installs, projects without a local dependency), so a
|
|
16
16
|
* real install in the user's project keeps precedence.
|
|
17
|
+
*
|
|
18
|
+
* A third case is first-party factory agents that relative-import this
|
|
19
|
+
* package's `src/` from a monorepo checkout. Hosted engines `npm install`
|
|
20
|
+
* only the agent directory, so files under `packages/agent-serve/src` have
|
|
21
|
+
* no `node_modules`. Bare specifiers from that tree (`zod`, octokit, …)
|
|
22
|
+
* retry against the running `@cursor/july` package.json, which does.
|
|
17
23
|
*/
|
|
18
24
|
|
|
19
25
|
// Null-prototype copies so specifiers like "constructor" or "__proto__"
|
|
20
26
|
// can never resolve to inherited Object.prototype members.
|
|
21
27
|
let directAliases = toNullProtoMap({});
|
|
22
28
|
let fallbackAliases = toNullProtoMap({});
|
|
29
|
+
let sourceTreeFallbackParentURL: string | undefined;
|
|
23
30
|
|
|
24
31
|
export interface AuthoredAliasHookData {
|
|
25
32
|
directAliases: Record<string, string>;
|
|
26
33
|
fallbackAliases: Record<string, string>;
|
|
34
|
+
/**
|
|
35
|
+
* `file:` URL of the running `@cursor/july` package.json. Bare imports
|
|
36
|
+
* from monorepo `packages/agent-serve/` source retry against this parent
|
|
37
|
+
* when the source tree has no `node_modules` (hosted factory engines).
|
|
38
|
+
*/
|
|
39
|
+
sourceTreeFallbackParentURL?: string;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
function toNullProtoMap(
|
|
@@ -35,6 +48,9 @@ function toNullProtoMap(
|
|
|
35
48
|
export function initialize(data: AuthoredAliasHookData): void {
|
|
36
49
|
directAliases = toNullProtoMap(data.directAliases);
|
|
37
50
|
fallbackAliases = toNullProtoMap(data.fallbackAliases);
|
|
51
|
+
const parent = data.sourceTreeFallbackParentURL;
|
|
52
|
+
sourceTreeFallbackParentURL =
|
|
53
|
+
typeof parent === "string" && parent !== "" ? parent : undefined;
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
interface ResolveResult {
|
|
@@ -58,12 +74,64 @@ export function resolve(
|
|
|
58
74
|
return { url: direct, shortCircuit: true };
|
|
59
75
|
}
|
|
60
76
|
const fallback = fallbackAliases[specifier];
|
|
61
|
-
if (fallback
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
77
|
+
if (fallback !== undefined) {
|
|
78
|
+
return Promise.resolve()
|
|
79
|
+
.then(() => nextResolve(specifier, context))
|
|
80
|
+
.catch(() => ({ url: fallback, shortCircuit: true }));
|
|
81
|
+
}
|
|
82
|
+
if (
|
|
83
|
+
sourceTreeFallbackParentURL !== undefined &&
|
|
84
|
+
isBarePackageSpecifier(specifier) &&
|
|
85
|
+
isMonorepoAgentServeSource(parentURLOf(context))
|
|
86
|
+
) {
|
|
87
|
+
const retryParent = sourceTreeFallbackParentURL;
|
|
88
|
+
return Promise.resolve()
|
|
89
|
+
.then(() => nextResolve(specifier, context))
|
|
90
|
+
.catch(() => nextResolve(specifier, withParentURL(context, retryParent)));
|
|
91
|
+
}
|
|
92
|
+
// Not ours: pass through with no promise/try-catch overhead. This hook
|
|
93
|
+
// runs on every module resolution in the process.
|
|
94
|
+
return nextResolve(specifier, context);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function parentURLOf(context: unknown): string | undefined {
|
|
98
|
+
if (context === null || typeof context !== "object") {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const parentURL = (context as { parentURL?: unknown }).parentURL;
|
|
102
|
+
return typeof parentURL === "string" ? parentURL : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function withParentURL(context: unknown, parentURL: string): unknown {
|
|
106
|
+
if (context !== null && typeof context === "object") {
|
|
107
|
+
return { ...(context as Record<string, unknown>), parentURL };
|
|
108
|
+
}
|
|
109
|
+
return { parentURL };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isBarePackageSpecifier(specifier: string): boolean {
|
|
113
|
+
if (specifier.length === 0) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const first = specifier[0];
|
|
117
|
+
if (first === "." || first === "/" || first === "\\") {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return !(
|
|
121
|
+
specifier.startsWith("node:") ||
|
|
122
|
+
specifier.startsWith("file:") ||
|
|
123
|
+
specifier.startsWith("data:") ||
|
|
124
|
+
specifier.startsWith("http:") ||
|
|
125
|
+
specifier.startsWith("https:")
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isMonorepoAgentServeSource(parentURL: string | undefined): boolean {
|
|
130
|
+
if (parentURL === undefined) {
|
|
131
|
+
return false;
|
|
65
132
|
}
|
|
66
|
-
return
|
|
67
|
-
.
|
|
68
|
-
.
|
|
133
|
+
return (
|
|
134
|
+
parentURL.includes("/packages/agent-serve/") &&
|
|
135
|
+
!parentURL.includes("/node_modules/")
|
|
136
|
+
);
|
|
69
137
|
}
|
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
* imports of the framework resolve against the running package: the
|
|
12
12
|
* historical `@anysphere/agent-serve[/x]` name always, and
|
|
13
13
|
* `@cursor/july[/x]` / `@cursor/agentkit[/x]` as fallbacks when the user's
|
|
14
|
-
* project has no local install (npx / global).
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* project has no local install (npx / global). The same hooks retry bare
|
|
15
|
+
* npm specifiers from monorepo `packages/agent-serve/` source against this
|
|
16
|
+
* package (hosted factory engines install only the agent directory).
|
|
17
|
+
* Source runs (tsx from the monorepo) skip both — the host loader and
|
|
18
|
+
* tsconfig paths already cover them.
|
|
17
19
|
*/
|
|
18
20
|
|
|
19
21
|
import { stat } from "node:fs/promises";
|
|
@@ -175,7 +177,16 @@ function registerFrameworkAliases(): void {
|
|
|
175
177
|
fallbackAliases[`${AGENTKIT_PACKAGE_NAME}${suffix}`] = target;
|
|
176
178
|
}
|
|
177
179
|
const hooksUrl = new URL("./authored-alias-hooks.js", import.meta.url);
|
|
178
|
-
const data: AuthoredAliasHookData = {
|
|
180
|
+
const data: AuthoredAliasHookData = {
|
|
181
|
+
directAliases,
|
|
182
|
+
fallbackAliases,
|
|
183
|
+
// Hosted factory agents relative-import this package's src/ from a
|
|
184
|
+
// monorepo checkout while npm install ran only in the agent directory.
|
|
185
|
+
// Bare specifiers from that tree retry against this package.json.
|
|
186
|
+
sourceTreeFallbackParentURL: pathToFileURL(
|
|
187
|
+
join(packageRootDir(), "package.json")
|
|
188
|
+
).href,
|
|
189
|
+
};
|
|
179
190
|
// `node:module`'s register() is synchronous: it blocks until the hooks
|
|
180
191
|
// thread has initialized, so the aliases are active when it returns.
|
|
181
192
|
registerModuleHooks(hooksUrl, { data });
|