@cursor/july 0.1.78 → 0.1.80
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/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +8 -1
- package/dist/channels/github/cursor-account.d.ts +17 -0
- package/dist/channels/github/cursor-account.d.ts.map +1 -1
- package/dist/channels/github/cursor-account.js +47 -0
- package/dist/docs/404.html +1 -1
- package/dist/docs/ab.html +2 -2
- package/dist/docs/assets/{app.BsA7t7rS.js → app.e4_wtjUS.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BGiMJpnL.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.B1hHhktM.js → VPLocalSearchBox.DbdzyEie.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DLpLSsQh.js → theme.D8HpLrLK.js} +2 -2
- package/dist/docs/building-with-agents.html +2 -2
- package/dist/docs/concepts.html +2 -2
- package/dist/docs/deployment.html +2 -2
- package/dist/docs/evals.html +2 -2
- package/dist/docs/example-agents/approval-buddy.html +2 -2
- package/dist/docs/example-agents/benny.html +2 -2
- package/dist/docs/example-agents/bugbot.html +2 -2
- package/dist/docs/example-agents/codebase-wiki.html +2 -2
- package/dist/docs/example-agents/codeowners-review.html +2 -2
- package/dist/docs/example-agents/concierge.html +2 -2
- package/dist/docs/example-agents/fsd.html +2 -2
- package/dist/docs/example-agents/index.html +2 -2
- package/dist/docs/example-agents/knowledge-base.html +2 -2
- package/dist/docs/example-agents/oncall.html +2 -2
- package/dist/docs/example-agents/security-reviewer.html +2 -2
- package/dist/docs/example-agents/slack-agent.html +2 -2
- package/dist/docs/example-agents/weather-agent.html +2 -2
- package/dist/docs/guides/agent-to-agent.html +2 -2
- package/dist/docs/guides/cloud-runtime.html +2 -2
- package/dist/docs/guides/convert-automation.html +2 -2
- package/dist/docs/guides/github.html +2 -2
- package/dist/docs/guides/human-in-the-loop.html +2 -2
- package/dist/docs/guides/mcp-oauth.html +2 -2
- package/dist/docs/guides/opentelemetry.html +2 -2
- package/dist/docs/guides/slack.html +2 -2
- package/dist/docs/guides/webhooks.html +2 -2
- package/dist/docs/hillclimbing.html +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/reference/agent-config.html +2 -2
- package/dist/docs/reference/artifacts.html +2 -2
- package/dist/docs/reference/channels.html +2 -2
- package/dist/docs/reference/cli.html +2 -2
- package/dist/docs/reference/connections.html +2 -2
- package/dist/docs/reference/hooks.html +2 -2
- package/dist/docs/reference/http-api.html +2 -2
- package/dist/docs/reference/instructions.html +2 -2
- package/dist/docs/reference/playground.html +2 -2
- package/dist/docs/reference/project-layout.html +2 -2
- package/dist/docs/reference/prompt.html +2 -2
- package/dist/docs/reference/schedules.html +2 -2
- package/dist/docs/reference/sessions.html +2 -2
- package/dist/docs/reference/skills.html +2 -2
- package/dist/docs/reference/subagents.html +2 -2
- package/dist/docs/reference/tools.html +2 -2
- package/dist/docs/scaffolding-agents.html +2 -2
- package/dist/docs/storage.html +2 -2
- package/dist/docs/templates/agentic-owners.html +2 -2
- package/dist/docs/templates/demo.html +2 -2
- package/dist/docs/templates/pr-autofixer.html +2 -2
- package/dist/docs/templates/security-reviewer.html +2 -2
- package/dist/docs/templates/triage.html +2 -2
- package/dist/docs/troubleshooting.html +2 -2
- package/dist/internal/review-comments.d.ts +37 -7
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +209 -10
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +101 -49
- package/package.json +1 -1
- package/src/channels/github/api.ts +8 -0
- package/src/channels/github/cursor-account.ts +56 -0
- package/src/internal/review-comments.ts +257 -16
- package/src/internal/server.ts +124 -60
- package/templates/agentic-owners/agent/lib/owners/match.ts +64 -3
- package/templates/agentic-owners/agent/lib/owners/parse.ts +55 -1
- package/templates/agentic-owners/agent/lib/owners/types.ts +5 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.Dcs6boxD.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HostGitHubClient } from "../types.js";
|
|
2
|
-
export type ReviewCommentPublisher = "local_gh" | "host_github";
|
|
2
|
+
export type ReviewCommentPublisher = "local_gh" | "host_github" | "host_origin";
|
|
3
3
|
export type ReviewCommentPostResult = {
|
|
4
4
|
posted: boolean;
|
|
5
5
|
publisher: ReviewCommentPublisher;
|
|
@@ -48,12 +48,24 @@ export type ReviewCommentPublisherImplementation = {
|
|
|
48
48
|
postReview: (args: ReviewCommentPostArgs) => Promise<ReviewCommentPostResult>;
|
|
49
49
|
};
|
|
50
50
|
/**
|
|
51
|
-
* SCMs this module can
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
51
|
+
* SCMs this module can address. Same growth seam as `shallow-clone.ts`'s
|
|
52
|
+
* {@link import("./shallow-clone.js").ScmProvider}: adding a forge means a
|
|
53
|
+
* resolver case plus a publisher (and, when the public API supports it, a
|
|
54
|
+
* reader / thread resolver). Callers above this line stay provider-neutral.
|
|
55
55
|
*/
|
|
56
|
-
export type ScmReviewProvider = "github";
|
|
56
|
+
export type ScmReviewProvider = "github" | "origin";
|
|
57
|
+
/**
|
|
58
|
+
* Origin public REST client used to post PR reviews (`ctx.origin` /
|
|
59
|
+
* {@link import("../channels/origin/api.js").createOriginClient}). Narrower
|
|
60
|
+
* than the full Origin handle so tests and CLI paths can inject `request()`.
|
|
61
|
+
*/
|
|
62
|
+
export type HostOriginReviewClient = {
|
|
63
|
+
request(input: {
|
|
64
|
+
method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
|
|
65
|
+
path: string;
|
|
66
|
+
body?: unknown;
|
|
67
|
+
}): Promise<unknown>;
|
|
68
|
+
};
|
|
57
69
|
/** Which SCM's PR to read review comments from. */
|
|
58
70
|
export type ScmReviewTarget = {
|
|
59
71
|
provider: ScmReviewProvider;
|
|
@@ -126,7 +138,7 @@ export type ResolveReviewThreadsResult = {
|
|
|
126
138
|
export type ReviewThreadResolverImplementation = {
|
|
127
139
|
resolveReviewThreadsForComments: (args: ResolveReviewThreadsArgs) => Promise<ResolveReviewThreadsResult>;
|
|
128
140
|
};
|
|
129
|
-
/** Parse a PR URL into the SCM target to read comments
|
|
141
|
+
/** Parse a PR URL into the SCM target to read or post comments on. */
|
|
130
142
|
export declare function resolveScmReviewTarget(prUrl: string): ScmReviewTarget | undefined;
|
|
131
143
|
/**
|
|
132
144
|
* Reader for a resolved target, mirroring Bugbot's `createPrGitProvider`
|
|
@@ -213,5 +225,23 @@ export declare function createLocalGhReviewPublisher(): ReviewCommentPublisherIm
|
|
|
213
225
|
* cursorAccount })` is bound.
|
|
214
226
|
*/
|
|
215
227
|
export declare function createHostGithubReviewPublisher(github: HostGitHubClient): ReviewCommentPublisherImplementation;
|
|
228
|
+
/**
|
|
229
|
+
* Pick the publisher for `prUrl`'s SCM. GitHub falls back to local `gh`
|
|
230
|
+
* when no host client is bound (CLI). Origin has no local CLI publisher —
|
|
231
|
+
* returns undefined unless an Origin REST client is provided.
|
|
232
|
+
*/
|
|
233
|
+
export declare function createReviewCommentPublisher(args: {
|
|
234
|
+
prUrl: string;
|
|
235
|
+
github?: HostGitHubClient;
|
|
236
|
+
origin?: HostOriginReviewClient;
|
|
237
|
+
}): ReviewCommentPublisherImplementation | undefined;
|
|
238
|
+
/**
|
|
239
|
+
* Posts a COMMENT-verdict Origin review through the public REST API.
|
|
240
|
+
*
|
|
241
|
+
* Public Origin REST has no diff-anchored review comments (those are an
|
|
242
|
+
* internal CreateCommentThread RPC). Inline comments in the request are
|
|
243
|
+
* folded into the review body so findings still land on the PR.
|
|
244
|
+
*/
|
|
245
|
+
export declare function createHostOriginReviewPublisher(origin: HostOriginReviewClient): ReviewCommentPublisherImplementation;
|
|
216
246
|
export {};
|
|
217
247
|
//# sourceMappingURL=review-comments.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-comments.d.ts","sourceRoot":"","sources":["../../src/internal/review-comments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"review-comments.d.ts","sourceRoot":"","sources":["../../src/internal/review-comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,16 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
|
-
/** Parse a PR URL into the SCM target to read comments
|
|
11
|
+
/** Parse a PR URL into the SCM target to read or post comments on. */
|
|
12
12
|
export function resolveScmReviewTarget(prUrl) {
|
|
13
|
-
const parsed =
|
|
14
|
-
return parsed.kind === "valid"
|
|
15
|
-
? {
|
|
16
|
-
provider: "github",
|
|
17
|
-
repo: parsed.value.repo,
|
|
18
|
-
prNumber: parsed.value.prNumber,
|
|
19
|
-
}
|
|
20
|
-
: undefined;
|
|
13
|
+
const parsed = parseReviewPrUrl(prUrl);
|
|
14
|
+
return parsed.kind === "valid" ? parsed.value : undefined;
|
|
21
15
|
}
|
|
22
16
|
/**
|
|
23
17
|
* Reader for a resolved target, mirroring Bugbot's `createPrGitProvider`
|
|
@@ -30,6 +24,10 @@ export function createReviewCommentReader(args) {
|
|
|
30
24
|
return args.github === undefined
|
|
31
25
|
? undefined
|
|
32
26
|
: createHostGithubReviewCommentReader(args.github);
|
|
27
|
+
case "origin":
|
|
28
|
+
// Public Origin REST lists discussion comments, not diff-anchored
|
|
29
|
+
// review threads. Stay undefined until a reader can fetch by id.
|
|
30
|
+
return undefined;
|
|
33
31
|
}
|
|
34
32
|
}
|
|
35
33
|
/**
|
|
@@ -42,6 +40,8 @@ export function createReviewThreadResolver(args) {
|
|
|
42
40
|
return args.github === undefined
|
|
43
41
|
? undefined
|
|
44
42
|
: createHostGithubReviewThreadResolver(args.github);
|
|
43
|
+
case "origin":
|
|
44
|
+
return undefined;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
@@ -66,7 +66,9 @@ export function resolveReviewThreadsForComments(args) {
|
|
|
66
66
|
github: args.github,
|
|
67
67
|
});
|
|
68
68
|
if (resolver === undefined) {
|
|
69
|
-
return skippedResolve(
|
|
69
|
+
return skippedResolve(target.provider === "github"
|
|
70
|
+
? "no GitHub client bound to resolve threads"
|
|
71
|
+
: `no thread resolver for ${target.provider}`);
|
|
70
72
|
}
|
|
71
73
|
return resolver.resolveReviewThreadsForComments({
|
|
72
74
|
target,
|
|
@@ -341,6 +343,43 @@ export function createHostGithubReviewPublisher(github) {
|
|
|
341
343
|
},
|
|
342
344
|
};
|
|
343
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Pick the publisher for `prUrl`'s SCM. GitHub falls back to local `gh`
|
|
348
|
+
* when no host client is bound (CLI). Origin has no local CLI publisher —
|
|
349
|
+
* returns undefined unless an Origin REST client is provided.
|
|
350
|
+
*/
|
|
351
|
+
export function createReviewCommentPublisher(args) {
|
|
352
|
+
const target = resolveScmReviewTarget(args.prUrl);
|
|
353
|
+
if (target === undefined) {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
switch (target.provider) {
|
|
357
|
+
case "github":
|
|
358
|
+
return args.github !== undefined
|
|
359
|
+
? createHostGithubReviewPublisher(args.github)
|
|
360
|
+
: createLocalGhReviewPublisher();
|
|
361
|
+
case "origin":
|
|
362
|
+
return args.origin === undefined
|
|
363
|
+
? undefined
|
|
364
|
+
: createHostOriginReviewPublisher(args.origin);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Posts a COMMENT-verdict Origin review through the public REST API.
|
|
369
|
+
*
|
|
370
|
+
* Public Origin REST has no diff-anchored review comments (those are an
|
|
371
|
+
* internal CreateCommentThread RPC). Inline comments in the request are
|
|
372
|
+
* folded into the review body so findings still land on the PR.
|
|
373
|
+
*/
|
|
374
|
+
export function createHostOriginReviewPublisher(origin) {
|
|
375
|
+
return {
|
|
376
|
+
postReview(args) {
|
|
377
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
378
|
+
return postReviewViaHostOrigin(origin, args);
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
344
383
|
function postReviewViaHostGithub(github, args) {
|
|
345
384
|
return __awaiter(this, void 0, void 0, function* () {
|
|
346
385
|
var _a, _b, _c;
|
|
@@ -523,6 +562,70 @@ function skippedHost(detail) {
|
|
|
523
562
|
detail,
|
|
524
563
|
};
|
|
525
564
|
}
|
|
565
|
+
function skippedOrigin(detail) {
|
|
566
|
+
return {
|
|
567
|
+
posted: false,
|
|
568
|
+
publisher: "host_origin",
|
|
569
|
+
mode: "skipped",
|
|
570
|
+
inlineCommentCount: 0,
|
|
571
|
+
detail,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function postReviewViaHostOrigin(origin, args) {
|
|
575
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
576
|
+
var _a, _b;
|
|
577
|
+
const target = resolveScmReviewTarget(args.prUrl);
|
|
578
|
+
if (target === undefined || target.provider !== "origin") {
|
|
579
|
+
return skippedOrigin("host Origin publisher only supports Origin PRs");
|
|
580
|
+
}
|
|
581
|
+
const [owner, repo] = target.repo.split("/", 2);
|
|
582
|
+
if (owner === undefined || repo === undefined || repo === "") {
|
|
583
|
+
return skippedOrigin("unparseable owner/repo from PR URL");
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
(_a = args.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
|
|
587
|
+
yield origin.request({
|
|
588
|
+
method: "POST",
|
|
589
|
+
path: `/v1/origin/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${target.prNumber}/reviews`,
|
|
590
|
+
body: {
|
|
591
|
+
verdict: "comment",
|
|
592
|
+
body: originReviewBody(args),
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
return {
|
|
596
|
+
posted: true,
|
|
597
|
+
publisher: "host_origin",
|
|
598
|
+
mode: "review_body_only",
|
|
599
|
+
inlineCommentCount: 0,
|
|
600
|
+
detail: `posted Origin review on ${target.repo}#${target.prNumber}`,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
if ((_b = args.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
return skippedOrigin(`host Origin review post failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Origin's public CreatePullRequestReview has a body, not inline comments.
|
|
613
|
+
* Fold attempted anchors into the summary so findings are not dropped.
|
|
614
|
+
*/
|
|
615
|
+
function originReviewBody(args) {
|
|
616
|
+
if (args.comments.length === 0) {
|
|
617
|
+
return args.message;
|
|
618
|
+
}
|
|
619
|
+
const sections = args.comments.map((comment) => {
|
|
620
|
+
const location = comment.start_line !== undefined &&
|
|
621
|
+
comment.start_line > 0 &&
|
|
622
|
+
comment.start_line !== comment.line
|
|
623
|
+
? `${comment.file}:${comment.start_line}-${comment.line}`
|
|
624
|
+
: `${comment.file}:${comment.line}`;
|
|
625
|
+
return `### \`${location}\`\n\n${comment.body}`;
|
|
626
|
+
});
|
|
627
|
+
return [args.message, ...sections].join("\n\n");
|
|
628
|
+
}
|
|
526
629
|
function skippedResolve(detail) {
|
|
527
630
|
return {
|
|
528
631
|
requested: 0,
|
|
@@ -533,6 +636,102 @@ function skippedResolve(detail) {
|
|
|
533
636
|
detail,
|
|
534
637
|
};
|
|
535
638
|
}
|
|
639
|
+
const ORIGIN_PR_PAGE_HOST_PATTERN = /^origin(-[a-z0-9]+)?\.cursor\.com$/i;
|
|
640
|
+
/**
|
|
641
|
+
* Provider-neutral PR URL parse. GitHub.com plus the Origin page shapes
|
|
642
|
+
* `parsePrTarget` in security-review-lib accepts. Duplicated here so
|
|
643
|
+
* agent-serve does not import that package.
|
|
644
|
+
*/
|
|
645
|
+
function parseReviewPrUrl(prUrl) {
|
|
646
|
+
let url;
|
|
647
|
+
try {
|
|
648
|
+
url = new URL(prUrl);
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
return {
|
|
652
|
+
kind: "invalid",
|
|
653
|
+
detail: `unparseable PR URL: ${error instanceof Error ? error.message : String(error)}`,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
657
|
+
const hostname = url.hostname;
|
|
658
|
+
if (hostname === "github.com" && parts.length >= 4 && parts[2] === "pull") {
|
|
659
|
+
const prNumber = Number(parts[3]);
|
|
660
|
+
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
661
|
+
return {
|
|
662
|
+
kind: "valid",
|
|
663
|
+
value: {
|
|
664
|
+
provider: "github",
|
|
665
|
+
repo: `${parts[0]}/${parts[1]}`,
|
|
666
|
+
prNumber,
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if (ORIGIN_PR_PAGE_HOST_PATTERN.test(hostname) &&
|
|
672
|
+
parts.length >= 4 &&
|
|
673
|
+
parts[2] === "github") {
|
|
674
|
+
const prNumber = Number(parts[3]);
|
|
675
|
+
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
676
|
+
return {
|
|
677
|
+
kind: "valid",
|
|
678
|
+
value: {
|
|
679
|
+
provider: "origin",
|
|
680
|
+
repo: `${parts[0]}/${parts[1]}`,
|
|
681
|
+
prNumber,
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (/^(www\.)?cursor\.com$/i.test(hostname) &&
|
|
687
|
+
parts[0] === "codebase" &&
|
|
688
|
+
parts.length >= 5 &&
|
|
689
|
+
parts[3] === "pull") {
|
|
690
|
+
const prNumber = Number(parts[4]);
|
|
691
|
+
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
692
|
+
return {
|
|
693
|
+
kind: "valid",
|
|
694
|
+
value: {
|
|
695
|
+
provider: "origin",
|
|
696
|
+
repo: `${parts[1]}/${parts[2]}`,
|
|
697
|
+
prNumber,
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (hostname === "review.cursor.com" && parts[0] === "pr") {
|
|
703
|
+
if (parts.length >= 5 && parts[3] === "github") {
|
|
704
|
+
const prNumber = Number(parts[4]);
|
|
705
|
+
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
706
|
+
return {
|
|
707
|
+
kind: "valid",
|
|
708
|
+
value: {
|
|
709
|
+
provider: "origin",
|
|
710
|
+
repo: `${parts[1]}/${parts[2]}`,
|
|
711
|
+
prNumber,
|
|
712
|
+
},
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (parts.length >= 4) {
|
|
717
|
+
const prNumber = Number(parts[3]);
|
|
718
|
+
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
719
|
+
return {
|
|
720
|
+
kind: "valid",
|
|
721
|
+
value: {
|
|
722
|
+
provider: "origin",
|
|
723
|
+
repo: `${parts[1]}/${parts[2]}`,
|
|
724
|
+
prNumber,
|
|
725
|
+
},
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
kind: "invalid",
|
|
732
|
+
detail: "unparseable PR URL: not a GitHub or Origin pull request",
|
|
733
|
+
};
|
|
734
|
+
}
|
|
536
735
|
function parseGithubPrUrl(prUrl) {
|
|
537
736
|
let url;
|
|
538
737
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwCH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAOhB,YAAY,EACb,MAAM,aAAa,CAAC;AAwHrB,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,iBAAiB,CAAC;AAwBpE,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;CACvB;AAaD,6EAA6E;AAC7E,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,GAAE,YAAY,GAAG,0BAA+B,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAwzB3B"}
|
package/dist/internal/server.js
CHANGED
|
@@ -36,7 +36,7 @@ import { once } from "node:events";
|
|
|
36
36
|
import { createServer, } from "node:http";
|
|
37
37
|
import { join } from "node:path";
|
|
38
38
|
import { dispatchDeploymentEvent, getDeploymentsChannelOptions, } from "../channels/deployments/deployments-channel.js";
|
|
39
|
-
import { createCursorAccountGithubTokenResolver, getGithubCursorAccountRuntime,
|
|
39
|
+
import { createCursorAccountGithubTokenResolver, cursorGithubPermissionsForChannel, getGithubCursorAccountRuntime, resolveCursorGithubCredentialScope, resolveCursorGithubPermissions, resolveCursorGithubProxyUrl, } from "../channels/github/cursor-account.js";
|
|
40
40
|
import { findGitHubChannelTargets } from "../channels/github/forward.js";
|
|
41
41
|
import { getOriginCursorAccountRuntime, parseCursorOriginRepoScope, } from "../channels/origin/cursor-account.js";
|
|
42
42
|
import { findOriginChannelTargets } from "../channels/origin/origin-channel.js";
|
|
@@ -160,53 +160,82 @@ export function startServer(mounts_1) {
|
|
|
160
160
|
? undefined
|
|
161
161
|
: requireCursorEventsAuth(effectiveCursorEvents, accountKey);
|
|
162
162
|
let cursorGithubCredentials;
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
if (cursorGithubTargets.length > 0) {
|
|
164
|
+
const githubProxyRequested = options.cursorGithubProxy === true;
|
|
165
|
+
const needsGithubAuth = cursorGithubConfig.scope !== undefined || githubProxyRequested;
|
|
166
|
+
if (needsGithubAuth) {
|
|
167
|
+
const cursorGithubAuth = cursorEventsAuth !== null && cursorEventsAuth !== void 0 ? cursorEventsAuth : ((accountKey === null || accountKey === void 0 ? void 0 : accountKey.apiKey) !== undefined && accountKey.apiKey.trim() !== ""
|
|
168
|
+
? {
|
|
169
|
+
apiKey: accountKey.apiKey.trim(),
|
|
170
|
+
baseUrl: cursorExternalApiUrl(),
|
|
171
|
+
}
|
|
172
|
+
: undefined);
|
|
173
|
+
if (cursorGithubAuth === undefined) {
|
|
174
|
+
throw new Error("githubChannel({ cursorAccount: true }) could not resolve Cursor event authentication.");
|
|
170
175
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const provider = new CursorGithubCredentialProvider({
|
|
177
|
-
client: new CursorBackendClient({
|
|
178
|
-
backendUrl: cursorEventsAuth.baseUrl,
|
|
179
|
-
apiKey: cursorEventsAuth.apiKey,
|
|
180
|
-
logger,
|
|
181
|
-
}),
|
|
182
|
-
scope: cursorGithubConfig.scope,
|
|
183
|
-
logger,
|
|
184
|
-
});
|
|
185
|
-
cursorGithubCredentials = provider;
|
|
186
|
-
const githubProxyUrl = options.cursorGithubProxy === true
|
|
187
|
-
? resolveCursorGithubProxyUrl(cursorEventsAuth.baseUrl)
|
|
188
|
-
: undefined;
|
|
189
|
-
if (githubProxyUrl !== undefined) {
|
|
190
|
-
// Proxy mode: channel Octokit calls carry the Cursor API key to the
|
|
191
|
-
// backend forwarder, which swaps in the installation token server-side
|
|
192
|
-
// and logs every call with per-deployment attribution. The provider
|
|
193
|
-
// still runs so child `gh` processes get a `GH_TOKEN` lease (v1).
|
|
194
|
-
const proxyTransport = {
|
|
195
|
-
apiBaseUrl: githubProxyUrl,
|
|
196
|
-
headers: {
|
|
197
|
-
"x-cursor-github-repos": cursorGithubConfig.scope.repos.join(","),
|
|
198
|
-
"x-cursor-github-permissions": cursorGithubConfig.scope.permissions,
|
|
199
|
-
},
|
|
200
|
-
};
|
|
201
|
-
for (const target of cursorGithubTargets) {
|
|
202
|
-
target.accountRuntime.credentials.bind(() => __awaiter(this, void 0, void 0, function* () { return cursorEventsAuth.apiKey; }));
|
|
203
|
-
target.accountRuntime.credentials.bindProxyTransport(proxyTransport);
|
|
176
|
+
if (options.allowAnonymous === true) {
|
|
177
|
+
if (options.allowAnonymousCursorGithub !== true) {
|
|
178
|
+
throw new Error("githubChannel({ cursorAccount: true }) cannot be served with --allow-anonymous because admitted callers could use the signed-in account's GitHub access. If every admitted caller is trusted (e.g. behind an SSO proxy), opt in with --allow-anonymous-cursor-github.");
|
|
179
|
+
}
|
|
180
|
+
logger("[agent-sdk] warning: --allow-anonymous-cursor-github — every admitted HTTP caller can drive sessions holding the signed-in account's repo-scoped GitHub credential; serve this only behind a trusted boundary (e.g. an SSO proxy)");
|
|
204
181
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
182
|
+
if (cursorGithubConfig.permissions === "contents-write") {
|
|
183
|
+
logger('[agent-sdk] warning: Cursor GitHub credentials include contents:write — the host holds a push-capable token shared with model-driven tools and untrusted webhook content; prefer permissions: "pr-write" unless this agent must push code');
|
|
184
|
+
}
|
|
185
|
+
const githubProxyUrl = githubProxyRequested
|
|
186
|
+
? resolveCursorGithubProxyUrl(cursorGithubAuth.baseUrl)
|
|
187
|
+
: undefined;
|
|
188
|
+
if (cursorGithubConfig.scope !== undefined) {
|
|
189
|
+
const provider = new CursorGithubCredentialProvider({
|
|
190
|
+
client: new CursorBackendClient({
|
|
191
|
+
backendUrl: cursorGithubAuth.baseUrl,
|
|
192
|
+
apiKey: cursorGithubAuth.apiKey,
|
|
193
|
+
logger,
|
|
194
|
+
}),
|
|
195
|
+
scope: cursorGithubConfig.scope,
|
|
196
|
+
logger,
|
|
197
|
+
});
|
|
198
|
+
cursorGithubCredentials = provider;
|
|
199
|
+
if (githubProxyUrl !== undefined) {
|
|
200
|
+
// Proxy mode: channel Octokit calls carry the Cursor API key to the
|
|
201
|
+
// backend forwarder, which swaps in the installation token server-side
|
|
202
|
+
// and logs every call with per-deployment attribution. The provider
|
|
203
|
+
// still runs so child `gh` processes get a `GH_TOKEN` lease (v1).
|
|
204
|
+
const proxyTransport = {
|
|
205
|
+
apiBaseUrl: githubProxyUrl,
|
|
206
|
+
headers: {
|
|
207
|
+
"x-cursor-github-repos": cursorGithubConfig.scope.repos.join(","),
|
|
208
|
+
"x-cursor-github-permissions": cursorGithubConfig.permissions,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
for (const target of cursorGithubTargets) {
|
|
212
|
+
target.accountRuntime.credentials.bind(() => __awaiter(this, void 0, void 0, function* () { return cursorGithubAuth.apiKey; }));
|
|
213
|
+
target.accountRuntime.credentials.bindProxyTransport(proxyTransport);
|
|
214
|
+
}
|
|
215
|
+
logger(`[agent-sdk] GitHub auth: channel API calls route through ${githubProxyUrl}`);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
for (const target of cursorGithubTargets) {
|
|
219
|
+
target.accountRuntime.credentials.bind(() => provider.getToken());
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (githubProxyUrl !== undefined) {
|
|
224
|
+
// `cursorAccount: true` with no `--repo`: skip the startup token mint
|
|
225
|
+
// (it requires a repo list) and let github-proxy take the repo from
|
|
226
|
+
// each Octokit call. Hosted multi-tenant uses this with the
|
|
227
|
+
// deployment event stream.
|
|
228
|
+
const proxyTransport = {
|
|
229
|
+
apiBaseUrl: githubProxyUrl,
|
|
230
|
+
headers: {
|
|
231
|
+
"x-cursor-github-permissions": cursorGithubConfig.permissions,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
for (const target of cursorGithubTargets) {
|
|
235
|
+
target.accountRuntime.credentials.bind(() => __awaiter(this, void 0, void 0, function* () { return cursorGithubAuth.apiKey; }));
|
|
236
|
+
target.accountRuntime.credentials.bindProxyTransport(proxyTransport);
|
|
237
|
+
}
|
|
238
|
+
logger(`[agent-sdk] GitHub auth: channel API calls route through ${githubProxyUrl} (repo scope per request)`);
|
|
210
239
|
}
|
|
211
240
|
}
|
|
212
241
|
}
|
|
@@ -1543,11 +1572,23 @@ function collectCursorGithubTargets(mounts) {
|
|
|
1543
1572
|
}
|
|
1544
1573
|
return targets;
|
|
1545
1574
|
}
|
|
1575
|
+
function cursorGithubPermissionsForTarget(target) {
|
|
1576
|
+
var _a;
|
|
1577
|
+
var _b;
|
|
1578
|
+
const authored = typeof target.accountRuntime.options.cursorAccount === "object"
|
|
1579
|
+
? target.accountRuntime.options.cursorAccount.permissions
|
|
1580
|
+
: undefined;
|
|
1581
|
+
return ((_b = (_a = target.accountRuntime.scope) === null || _a === void 0 ? void 0 : _a.permissions) !== null && _b !== void 0 ? _b : cursorGithubPermissionsForChannel(target.accountRuntime.options, authored));
|
|
1582
|
+
}
|
|
1546
1583
|
function resolveCursorGithubEventConfig(input) {
|
|
1547
1584
|
var _a;
|
|
1548
1585
|
var _b;
|
|
1549
1586
|
if (input.targets.length === 0) {
|
|
1550
|
-
return {
|
|
1587
|
+
return {
|
|
1588
|
+
cursorEvents: input.configured,
|
|
1589
|
+
scope: undefined,
|
|
1590
|
+
permissions: "pr-write",
|
|
1591
|
+
};
|
|
1551
1592
|
}
|
|
1552
1593
|
const repos = [
|
|
1553
1594
|
...((_b = (_a = input.configured) === null || _a === void 0 ? void 0 : _a.repos) !== null && _b !== void 0 ? _b : []),
|
|
@@ -1555,11 +1596,19 @@ function resolveCursorGithubEventConfig(input) {
|
|
|
1555
1596
|
...((_b = (_a = target.accountRuntime.scope) === null || _a === void 0 ? void 0 : _a.repos) !== null && _b !== void 0 ? _b : []),
|
|
1556
1597
|
]; }),
|
|
1557
1598
|
];
|
|
1558
|
-
const permissions = resolveCursorGithubPermissions(input.targets.map(
|
|
1559
|
-
const scope =
|
|
1599
|
+
const permissions = resolveCursorGithubPermissions(input.targets.map(cursorGithubPermissionsForTarget));
|
|
1600
|
+
const scope = resolveCursorGithubCredentialScope(repos, permissions);
|
|
1601
|
+
if (scope === undefined) {
|
|
1602
|
+
return {
|
|
1603
|
+
cursorEvents: input.configured,
|
|
1604
|
+
scope: undefined,
|
|
1605
|
+
permissions,
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1560
1608
|
return {
|
|
1561
1609
|
cursorEvents: Object.assign(Object.assign({}, input.configured), { repos: [...scope.repos] }),
|
|
1562
1610
|
scope,
|
|
1611
|
+
permissions,
|
|
1563
1612
|
};
|
|
1564
1613
|
}
|
|
1565
1614
|
function collectCursorOriginTargets(mounts) {
|
|
@@ -1591,6 +1640,9 @@ function resolveCursorOriginRepoScope(input) {
|
|
|
1591
1640
|
...(wantsHostRepos ? input.explicitRepos : []),
|
|
1592
1641
|
...input.targets.flatMap((target) => { var _a; var _b; return (_b = (_a = target.accountRuntime.scope) === null || _a === void 0 ? void 0 : _a.repos) !== null && _b !== void 0 ? _b : []; }),
|
|
1593
1642
|
];
|
|
1643
|
+
if (repos.length === 0) {
|
|
1644
|
+
return undefined;
|
|
1645
|
+
}
|
|
1594
1646
|
return parseCursorOriginRepoScope(repos);
|
|
1595
1647
|
}
|
|
1596
1648
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.80",
|
|
4
4
|
"description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"repository": {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
type CursorAccountGithubTokenResolver,
|
|
22
22
|
getCursorAccountGithubProxyTransport,
|
|
23
|
+
githubRepoFromOctokitRequest,
|
|
23
24
|
isCursorAccountGithubTokenResolver,
|
|
24
25
|
} from "./cursor-account.js";
|
|
25
26
|
import type { GitHubChannelCredentials } from "./types.js";
|
|
@@ -121,6 +122,13 @@ export async function createGitHubOctokit(input: {
|
|
|
121
122
|
request.headers.authorization = `Bearer ${token}`;
|
|
122
123
|
if (proxyTransport !== undefined) {
|
|
123
124
|
Object.assign(request.headers, proxyTransport.headers);
|
|
125
|
+
const existingRepos = request.headers["x-cursor-github-repos"];
|
|
126
|
+
if (typeof existingRepos !== "string" || existingRepos.trim() === "") {
|
|
127
|
+
const repo = githubRepoFromOctokitRequest(request);
|
|
128
|
+
if (repo !== undefined) {
|
|
129
|
+
request.headers["x-cursor-github-repos"] = repo;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
124
132
|
}
|
|
125
133
|
});
|
|
126
134
|
return octokit;
|
|
@@ -77,6 +77,62 @@ export function parseCursorGithubPermissions(
|
|
|
77
77
|
);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* `owner/repo` from an Octokit request, for github-proxy calls that have no
|
|
82
|
+
* static `x-cursor-github-repos` header (`cursorAccount: true` with no
|
|
83
|
+
* `--repo` / channel `{ repos }`).
|
|
84
|
+
*/
|
|
85
|
+
export function githubRepoFromOctokitRequest(request: {
|
|
86
|
+
owner?: unknown;
|
|
87
|
+
repo?: unknown;
|
|
88
|
+
url?: unknown;
|
|
89
|
+
}): string | undefined {
|
|
90
|
+
if (typeof request.owner === "string" && typeof request.repo === "string") {
|
|
91
|
+
const slug = `${request.owner.trim()}/${request.repo.trim()}`.toLowerCase();
|
|
92
|
+
if (CURSOR_GITHUB_REPO_PATTERN.test(slug) && !slug.includes("..")) {
|
|
93
|
+
return slug;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (typeof request.url !== "string") {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const match = request.url.match(/\/repos\/([^/?#]+)\/([^/?#]+)/);
|
|
100
|
+
const owner = match?.[1]?.trim() ?? "";
|
|
101
|
+
const repo = match?.[2]?.trim() ?? "";
|
|
102
|
+
if (
|
|
103
|
+
owner === "" ||
|
|
104
|
+
repo === "" ||
|
|
105
|
+
owner.includes("{") ||
|
|
106
|
+
repo.includes("{")
|
|
107
|
+
) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const slug = `${owner}/${repo}`.toLowerCase();
|
|
111
|
+
if (!CURSOR_GITHUB_REPO_PATTERN.test(slug) || slug.includes("..")) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
return slug;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Merge `--repo` / channel-declared repos into a lease scope. Empty is
|
|
119
|
+
* valid for `cursorAccount: true`: hosted engines that pull events from
|
|
120
|
+
* the deployment stream (no `--cursor-events --repo`) still start, and
|
|
121
|
+
* github-proxy fills `x-cursor-github-repos` per Octokit call.
|
|
122
|
+
*/
|
|
123
|
+
export function resolveCursorGithubCredentialScope(
|
|
124
|
+
values: readonly string[],
|
|
125
|
+
permissions: CursorGithubPermissions = "pr-write"
|
|
126
|
+
): CursorGithubRepoScope | undefined {
|
|
127
|
+
const repos = [
|
|
128
|
+
...new Set(values.map((repo) => repo.trim().toLowerCase()).filter(Boolean)),
|
|
129
|
+
];
|
|
130
|
+
if (repos.length === 0) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return parseCursorGithubRepoScope(repos, permissions);
|
|
134
|
+
}
|
|
135
|
+
|
|
80
136
|
export function parseCursorGithubRepoScope(
|
|
81
137
|
values: readonly string[],
|
|
82
138
|
permissions: CursorGithubPermissions = "pr-write"
|