@cursor/july 0.1.85 → 0.1.86
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/github-channel.d.ts +4 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +18 -11
- package/dist/channels/origin/origin-channel.d.ts +6 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +24 -11
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +12 -2
- package/dist/internal/continuation-identity.d.ts +10 -0
- package/dist/internal/continuation-identity.d.ts.map +1 -1
- package/dist/internal/continuation-identity.js +10 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +5 -0
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
- package/dist/internal/handleAgentServeTrigger.js +11 -3
- package/dist/internal/local-control-plane.d.ts +19 -0
- package/dist/internal/local-control-plane.d.ts.map +1 -1
- package/dist/internal/local-control-plane.js +17 -0
- package/dist/internal/local-ingress.d.ts +35 -0
- package/dist/internal/local-ingress.d.ts.map +1 -0
- package/dist/internal/local-ingress.js +44 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +138 -62
- package/dist/playground/assets/{index-CMe7imuc.css → index-C0_5hOsf.css} +1 -1
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
- package/src/channels/github/github-channel.ts +18 -21
- package/src/channels/origin/origin-channel.ts +26 -15
- package/src/channels/slack/dispatch.ts +11 -7
- package/src/internal/continuation-identity.ts +10 -0
- package/src/internal/handleAgentServeTrigger.ts +31 -2
- package/src/internal/local-control-plane.ts +33 -0
- package/src/internal/local-ingress.ts +90 -0
- package/src/internal/server.ts +181 -27
- package/src/types.ts +2 -2
- /package/dist/playground/assets/{index-JaFLlugp.js → index-Bqn91tW4.js} +0 -0
package/src/internal/server.ts
CHANGED
|
@@ -129,6 +129,7 @@ import {
|
|
|
129
129
|
createLocalControlPlane,
|
|
130
130
|
type LocalControlPlane,
|
|
131
131
|
} from "./local-control-plane.js";
|
|
132
|
+
import type { RouteLocalIngressContinuation } from "./local-ingress.js";
|
|
132
133
|
import { createLogRing, type LogRing } from "./log-ring.js";
|
|
133
134
|
import {
|
|
134
135
|
createAgentMcpEndpoint,
|
|
@@ -216,6 +217,28 @@ interface MountedRuntime {
|
|
|
216
217
|
evalRuns: EvalRunStore;
|
|
217
218
|
}
|
|
218
219
|
|
|
220
|
+
function localControlPlaneRoute(
|
|
221
|
+
runtimes: readonly MountedRuntime[]
|
|
222
|
+
): RouteLocalIngressContinuation | undefined {
|
|
223
|
+
for (const runtime of runtimes) {
|
|
224
|
+
if (runtime.controlPlane !== undefined) {
|
|
225
|
+
return (input) => runtime.controlPlane!.routeContinuation(input);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function firstLocalControlPlane(
|
|
232
|
+
runtimes: readonly MountedRuntime[]
|
|
233
|
+
): LocalControlPlane | undefined {
|
|
234
|
+
return runtimes.find((runtime) => runtime.controlPlane !== undefined)
|
|
235
|
+
?.controlPlane;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function isV2Project(project: { agent: { architecture?: string } }): boolean {
|
|
239
|
+
return project.agent.architecture === "v2";
|
|
240
|
+
}
|
|
241
|
+
|
|
219
242
|
/** Internal-only options for {@link startServer} (not on public `serve`). */
|
|
220
243
|
export interface StartServerInternalOptions {
|
|
221
244
|
/** Test seam: replace the Cursor SDK runner (integration tests). */
|
|
@@ -317,6 +340,20 @@ export async function startServer(
|
|
|
317
340
|
? undefined
|
|
318
341
|
: requireCursorEventsAuth(effectiveCursorEvents, accountKey);
|
|
319
342
|
|
|
343
|
+
// Two architectures + local CP + cursor-events would register two
|
|
344
|
+
// /v0/scm-events consumers (engine offset vs control-plane offset).
|
|
345
|
+
// Fail before engines or relays start. `--no-control-plane` mixed is one
|
|
346
|
+
// July consumer (v1 engine); hosted CP owns v2.
|
|
347
|
+
assertSingleCursorEventsLane({
|
|
348
|
+
noControlPlane,
|
|
349
|
+
config: effectiveCursorEvents,
|
|
350
|
+
auth: cursorEventsAuth,
|
|
351
|
+
v1Mounts: mounts.filter((mount) => !isV2Project(mount.project)),
|
|
352
|
+
v2Mounts: mounts.filter((mount) => isV2Project(mount.project)),
|
|
353
|
+
githubRepos: cursorGithubConfig.cursorEvents?.repos ?? [],
|
|
354
|
+
originRepos: cursorOriginScope?.repos ?? [],
|
|
355
|
+
});
|
|
356
|
+
|
|
320
357
|
let cursorGithubCredentials: CursorGithubCredentialProvider | undefined;
|
|
321
358
|
// Proxy-wired GitHub token resolver for `ctx.host.github` when no startup
|
|
322
359
|
// token mint runs (empty repo scope): host Octokit calls must route
|
|
@@ -755,36 +792,46 @@ export async function startServer(
|
|
|
755
792
|
// job (backend `/github_webhook` already verified); we only admit loopback
|
|
756
793
|
// so this route is not a public unsigned endpoint. Agents decide whether
|
|
757
794
|
// to run via their onPullRequest / onComment / … hooks.
|
|
758
|
-
const
|
|
795
|
+
const collectGitHubTargetsFrom = (selected: readonly MountedRuntime[]) =>
|
|
759
796
|
collectGitHubTriggerTargets({
|
|
760
|
-
mounts:
|
|
797
|
+
mounts: selected.map((runtime) => ({
|
|
761
798
|
slug: runtime.slug,
|
|
762
799
|
channels: runtime.project.channels,
|
|
763
800
|
buildHandlerArgs: (channelId) =>
|
|
764
801
|
buildChannelHandlerArgs({ runtime, channelId }),
|
|
765
802
|
})),
|
|
766
803
|
});
|
|
767
|
-
const
|
|
804
|
+
const collectOriginTargetsFrom = (selected: readonly MountedRuntime[]) =>
|
|
768
805
|
collectOriginTriggerTargets({
|
|
769
|
-
mounts:
|
|
806
|
+
mounts: selected.map((runtime) => ({
|
|
770
807
|
slug: runtime.slug,
|
|
771
808
|
channels: runtime.project.channels,
|
|
772
809
|
buildHandlerArgs: (channelId) =>
|
|
773
810
|
buildChannelHandlerArgs({ runtime, channelId }),
|
|
774
811
|
})),
|
|
775
812
|
});
|
|
813
|
+
const collectGitHubTargets = () => collectGitHubTargetsFrom(runtimes);
|
|
814
|
+
const collectOriginTargets = () => collectOriginTargetsFrom(runtimes);
|
|
815
|
+
const v1Runtimes = runtimes.filter(
|
|
816
|
+
(runtime) => !isV2Project(runtime.project)
|
|
817
|
+
);
|
|
818
|
+
const v2Runtimes = runtimes.filter((runtime) => isV2Project(runtime.project));
|
|
819
|
+
const v1Mounts = mounts.filter((mount) => !isV2Project(mount.project));
|
|
820
|
+
const v2Mounts = mounts.filter((mount) => isV2Project(mount.project));
|
|
776
821
|
router.add({
|
|
777
822
|
method: "POST",
|
|
778
823
|
pattern: "/v1/webhooks/github",
|
|
779
824
|
auth: [localDevStrict()],
|
|
780
825
|
handler: async (request) => {
|
|
781
826
|
const bodyText = await request.text();
|
|
827
|
+
const routeContinuation = localControlPlaneRoute(runtimes);
|
|
782
828
|
const result = await handleAgentServeTrigger({
|
|
783
829
|
type: "github",
|
|
784
830
|
bodyText,
|
|
785
831
|
eventName: request.headers.get("x-github-event"),
|
|
786
832
|
deliveryId: request.headers.get("x-github-delivery") ?? undefined,
|
|
787
833
|
targets: collectGitHubTargets(),
|
|
834
|
+
...(routeContinuation === undefined ? {} : { routeContinuation }),
|
|
788
835
|
});
|
|
789
836
|
const status = result.ok ? 202 : (result.targets[0]?.status ?? 500);
|
|
790
837
|
return jsonResponse(result, { status });
|
|
@@ -820,21 +867,48 @@ export async function startServer(
|
|
|
820
867
|
throw error;
|
|
821
868
|
}
|
|
822
869
|
|
|
823
|
-
// Cursor SCM events
|
|
824
|
-
|
|
870
|
+
// Cursor SCM events: v1 stays on the engine poll. v2 ingest is local-CP
|
|
871
|
+
// only — hosted `--no-control-plane` skips the July poll (hosted CP wakes).
|
|
872
|
+
const routeContinuation = localControlPlaneRoute(runtimes);
|
|
873
|
+
const cursorEventsCommon = {
|
|
825
874
|
config: effectiveCursorEvents,
|
|
826
875
|
githubRepos: cursorGithubConfig.cursorEvents?.repos ?? [],
|
|
827
876
|
originRepos: cursorOriginScope?.repos ?? [],
|
|
828
877
|
stateRoot: options.stateRoot,
|
|
829
|
-
mounts,
|
|
830
878
|
auth: cursorEventsAuth,
|
|
831
879
|
logger,
|
|
832
|
-
collectGitHubTargets,
|
|
833
|
-
collectOriginTargets,
|
|
834
880
|
cursorAccountOnly:
|
|
835
881
|
options.cursorEvents === undefined &&
|
|
836
882
|
(cursorGithubTargets.length > 0 || cursorOriginTargets.length > 0),
|
|
883
|
+
};
|
|
884
|
+
const cursorEventRelay = startCursorEventRelay({
|
|
885
|
+
...cursorEventsCommon,
|
|
886
|
+
mounts: v1Mounts,
|
|
887
|
+
collectGitHubTargets: () => collectGitHubTargetsFrom(v1Runtimes),
|
|
888
|
+
collectOriginTargets: () => collectOriginTargetsFrom(v1Runtimes),
|
|
889
|
+
lane: "engine",
|
|
890
|
+
skippedV2: v2Mounts.length > 0,
|
|
891
|
+
});
|
|
892
|
+
const controlPlaneScmRelay = startCursorEventRelay({
|
|
893
|
+
...cursorEventsCommon,
|
|
894
|
+
mounts: v2Mounts,
|
|
895
|
+
collectGitHubTargets: () => collectGitHubTargetsFrom(v2Runtimes),
|
|
896
|
+
collectOriginTargets: () => collectOriginTargetsFrom(v2Runtimes),
|
|
897
|
+
...(routeContinuation === undefined ? {} : { routeContinuation }),
|
|
898
|
+
lane: "control-plane",
|
|
899
|
+
start: false,
|
|
900
|
+
stateDirName: join("control-plane", "cursor-events"),
|
|
837
901
|
});
|
|
902
|
+
const localCp = firstLocalControlPlane(runtimes);
|
|
903
|
+
if (controlPlaneScmRelay !== undefined) {
|
|
904
|
+
if (localCp !== undefined) {
|
|
905
|
+
localCp.attachScmEvents(controlPlaneScmRelay);
|
|
906
|
+
} else {
|
|
907
|
+
logger(
|
|
908
|
+
"[agent-sdk] cursor-events: skipping v2 scm-events poll (--no-control-plane); hosted CP owns ingest"
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
838
912
|
|
|
839
913
|
// Cursor deployment-events relay: automatic when a mounted agent authored
|
|
840
914
|
// `deploymentsChannel({ ... })`.
|
|
@@ -866,6 +940,7 @@ export async function startServer(
|
|
|
866
940
|
} catch (error) {
|
|
867
941
|
await cursorDeploymentRelay?.stop();
|
|
868
942
|
await cursorEventRelay?.stop();
|
|
943
|
+
await controlPlaneScmRelay?.stop();
|
|
869
944
|
await cursorSlackRelay?.stop();
|
|
870
945
|
cursorGithubCredentials?.stop();
|
|
871
946
|
await shutdownOtelRuntime(otelRuntime);
|
|
@@ -1027,6 +1102,7 @@ export async function startServer(
|
|
|
1027
1102
|
playgroundDev?.stop();
|
|
1028
1103
|
await cursorDeploymentRelay?.stop();
|
|
1029
1104
|
await cursorEventRelay?.stop();
|
|
1105
|
+
await controlPlaneScmRelay?.stop();
|
|
1030
1106
|
await cursorSlackRelay?.stop();
|
|
1031
1107
|
for (const runtime of runtimes) {
|
|
1032
1108
|
runtime.controlPlane?.stop();
|
|
@@ -1878,24 +1954,13 @@ function requireCursorEventsAuth(
|
|
|
1878
1954
|
* channels; Origin is subscribed only when an `originChannel({ cursorAccount })`
|
|
1879
1955
|
* opted in.
|
|
1880
1956
|
*/
|
|
1881
|
-
function
|
|
1882
|
-
|
|
1957
|
+
function collectCursorEventSubscriptions(input: {
|
|
1958
|
+
mounts: readonly AgentMount[];
|
|
1883
1959
|
githubRepos: readonly string[];
|
|
1884
1960
|
originRepos: readonly string[];
|
|
1885
|
-
|
|
1886
|
-
mounts
|
|
1887
|
-
|
|
1888
|
-
logger: (line: string) => void;
|
|
1889
|
-
collectGitHubTargets: () => ReturnType<typeof collectGitHubTriggerTargets>;
|
|
1890
|
-
collectOriginTargets: () => ReturnType<typeof collectOriginTriggerTargets>;
|
|
1891
|
-
cursorAccountOnly: boolean;
|
|
1892
|
-
}): CursorEventRelay | undefined {
|
|
1893
|
-
const { config, stateRoot, mounts, auth, logger } = input;
|
|
1894
|
-
if (config === undefined || auth === undefined) {
|
|
1895
|
-
return undefined;
|
|
1896
|
-
}
|
|
1897
|
-
const githubTargets = findGitHubChannelTargets(mounts);
|
|
1898
|
-
const originTargets = findOriginChannelTargets(mounts);
|
|
1961
|
+
}): CursorScmSubscription[] {
|
|
1962
|
+
const githubTargets = findGitHubChannelTargets(input.mounts);
|
|
1963
|
+
const originTargets = findOriginChannelTargets(input.mounts);
|
|
1899
1964
|
const githubEvents = [
|
|
1900
1965
|
...new Set(githubTargets.flatMap((target) => target.events)),
|
|
1901
1966
|
];
|
|
@@ -1928,8 +1993,88 @@ function startCursorEventRelay(input: {
|
|
|
1928
1993
|
});
|
|
1929
1994
|
}
|
|
1930
1995
|
}
|
|
1996
|
+
return subscriptions;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
/**
|
|
2000
|
+
* Local CP + `--cursor-events` + both architectures would start two
|
|
2001
|
+
* consumers (different offset dirs). Refuse that at serve start.
|
|
2002
|
+
* `--no-control-plane` is one July consumer — hosted CP owns v2.
|
|
2003
|
+
*/
|
|
2004
|
+
function assertSingleCursorEventsLane(input: {
|
|
2005
|
+
noControlPlane: boolean;
|
|
2006
|
+
config: ServeOptions["cursorEvents"];
|
|
2007
|
+
auth: CursorEventsAuth | undefined;
|
|
2008
|
+
v1Mounts: readonly AgentMount[];
|
|
2009
|
+
v2Mounts: readonly AgentMount[];
|
|
2010
|
+
githubRepos: readonly string[];
|
|
2011
|
+
originRepos: readonly string[];
|
|
2012
|
+
}): void {
|
|
2013
|
+
if (
|
|
2014
|
+
input.noControlPlane ||
|
|
2015
|
+
input.config === undefined ||
|
|
2016
|
+
input.auth === undefined
|
|
2017
|
+
) {
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
const shared = {
|
|
2021
|
+
githubRepos: input.githubRepos,
|
|
2022
|
+
originRepos: input.originRepos,
|
|
2023
|
+
};
|
|
2024
|
+
if (
|
|
2025
|
+
collectCursorEventSubscriptions({ mounts: input.v1Mounts, ...shared })
|
|
2026
|
+
.length === 0 ||
|
|
2027
|
+
collectCursorEventSubscriptions({ mounts: input.v2Mounts, ...shared })
|
|
2028
|
+
.length === 0
|
|
2029
|
+
) {
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
throw new Error(
|
|
2033
|
+
"[agent-sdk] cursor-events: refusing to serve v1 and v2 github/origin agents in one process — they would open two /v0/scm-events consumers. Serve one architecture per process."
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
function startCursorEventRelay(input: {
|
|
2038
|
+
config: ServeOptions["cursorEvents"];
|
|
2039
|
+
githubRepos: readonly string[];
|
|
2040
|
+
originRepos: readonly string[];
|
|
2041
|
+
stateRoot: string | undefined;
|
|
2042
|
+
mounts: AgentMount[];
|
|
2043
|
+
auth: CursorEventsAuth | undefined;
|
|
2044
|
+
logger: (line: string) => void;
|
|
2045
|
+
collectGitHubTargets: () => ReturnType<typeof collectGitHubTriggerTargets>;
|
|
2046
|
+
collectOriginTargets: () => ReturnType<typeof collectOriginTriggerTargets>;
|
|
2047
|
+
routeContinuation?: RouteLocalIngressContinuation;
|
|
2048
|
+
cursorAccountOnly: boolean;
|
|
2049
|
+
/** Engine = v1 poll. Control-plane = v2 poll (started by local CP). */
|
|
2050
|
+
lane: "engine" | "control-plane";
|
|
2051
|
+
/** When the engine lane has no v1 targets, mention skipped v2 mounts. */
|
|
2052
|
+
skippedV2?: boolean;
|
|
2053
|
+
/** When false, return an unstarted relay (local CP calls start). */
|
|
2054
|
+
start?: boolean;
|
|
2055
|
+
/** Offset dir under stateRoot. Defaults to `cursor-events`. */
|
|
2056
|
+
stateDirName?: string;
|
|
2057
|
+
}): CursorEventRelay | undefined {
|
|
2058
|
+
const { config, stateRoot, mounts, auth, logger } = input;
|
|
2059
|
+
if (config === undefined || auth === undefined) {
|
|
2060
|
+
return undefined;
|
|
2061
|
+
}
|
|
2062
|
+
const subscriptions = collectCursorEventSubscriptions({
|
|
2063
|
+
mounts,
|
|
2064
|
+
githubRepos: input.githubRepos,
|
|
2065
|
+
originRepos: input.originRepos,
|
|
2066
|
+
});
|
|
1931
2067
|
if (subscriptions.length === 0) {
|
|
1932
|
-
if (
|
|
2068
|
+
if (input.lane === "engine" && input.skippedV2 === true) {
|
|
2069
|
+
logger(
|
|
2070
|
+
"[agent-sdk] cursor-events: no v1 github/origin targets; v2 ingest is control-plane only"
|
|
2071
|
+
);
|
|
2072
|
+
} else if (input.lane === "control-plane") {
|
|
2073
|
+
return undefined;
|
|
2074
|
+
} else if (
|
|
2075
|
+
input.githubRepos.every((repo) => repo.trim() === "") &&
|
|
2076
|
+
input.originRepos.every((repo) => repo.trim() === "")
|
|
2077
|
+
) {
|
|
1933
2078
|
logger(
|
|
1934
2079
|
"[agent-sdk] cursor-events: no repos configured (pass --repo owner/name); relay disabled"
|
|
1935
2080
|
);
|
|
@@ -1947,7 +2092,7 @@ function startCursorEventRelay(input: {
|
|
|
1947
2092
|
baseUrl: auth.baseUrl,
|
|
1948
2093
|
apiKey: auth.apiKey,
|
|
1949
2094
|
subscriptions,
|
|
1950
|
-
stateDir: join(baseStateRoot, "cursor-events"),
|
|
2095
|
+
stateDir: join(baseStateRoot, input.stateDirName ?? "cursor-events"),
|
|
1951
2096
|
logger,
|
|
1952
2097
|
dispatch: async (event) => {
|
|
1953
2098
|
if (event.provider === "origin") {
|
|
@@ -1970,6 +2115,9 @@ function startCursorEventRelay(input: {
|
|
|
1970
2115
|
type: "origin",
|
|
1971
2116
|
bodyText: JSON.stringify(scmMetadataToOriginPayload(event)),
|
|
1972
2117
|
targets,
|
|
2118
|
+
...(input.routeContinuation === undefined
|
|
2119
|
+
? {}
|
|
2120
|
+
: { routeContinuation: input.routeContinuation }),
|
|
1973
2121
|
});
|
|
1974
2122
|
logCursorScmDispatch(logger, event, result);
|
|
1975
2123
|
return;
|
|
@@ -1996,10 +2144,16 @@ function startCursorEventRelay(input: {
|
|
|
1996
2144
|
eventName: event.eventName,
|
|
1997
2145
|
deliveryId: event.deliveryId,
|
|
1998
2146
|
targets,
|
|
2147
|
+
...(input.routeContinuation === undefined
|
|
2148
|
+
? {}
|
|
2149
|
+
: { routeContinuation: input.routeContinuation }),
|
|
1999
2150
|
});
|
|
2000
2151
|
logCursorScmDispatch(logger, event, result);
|
|
2001
2152
|
},
|
|
2002
2153
|
});
|
|
2154
|
+
if (input.start === false) {
|
|
2155
|
+
return relay;
|
|
2156
|
+
}
|
|
2003
2157
|
relay.start();
|
|
2004
2158
|
logger(
|
|
2005
2159
|
`[agent-sdk] cursor-events: relaying ${subscriptions.length} subscription(s) from ${auth.baseUrl}`
|
package/src/types.ts
CHANGED
|
@@ -1689,8 +1689,8 @@ export interface SendMessageOptions {
|
|
|
1689
1689
|
*/
|
|
1690
1690
|
continuationToken?: string;
|
|
1691
1691
|
/**
|
|
1692
|
-
* v2 mailbox stamp from the control plane (
|
|
1693
|
-
*
|
|
1692
|
+
* v2 mailbox stamp from the control plane (hosted verified ingress or
|
|
1693
|
+
* the local CP). Channel adapters do not extract. Ignored on v1.
|
|
1694
1694
|
*/
|
|
1695
1695
|
mailboxContinuation?: MailboxContinuation;
|
|
1696
1696
|
/**
|
|
File without changes
|