@cursor/july 0.1.84 → 0.1.85
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/deployments/deployments-channel.d.ts +2 -2
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +7 -6
- package/dist/channels/deployments/index.d.ts +2 -3
- package/dist/channels/deployments/index.d.ts.map +1 -1
- package/dist/channels/deployments/index.js +2 -3
- package/dist/channels/deployments/types.d.ts +14 -14
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.kqdC1N7p.js → app.DUOPbN18.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.CxCtxfDE.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DXDE2WgG.js → VPLocalSearchBox.CmWbGcGk.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DFYqbyta.js → theme.S57OeOLA.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.G5GysZ7e.js → reference_channels.md.CboFd5IH.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/cursor-deployment-relay.d.ts +4 -4
- package/dist/internal/cursor-deployment-relay.d.ts.map +1 -1
- package/dist/internal/cursor-deployment-relay.js +33 -32
- package/dist/internal/server.js +13 -13
- package/dist/playground/assets/index-CMe7imuc.css +1 -0
- package/dist/playground/assets/{index-Cspgk9jC.js → index-JaFLlugp.js} +45 -45
- package/dist/playground/index.html +2 -2
- package/docs/reference/channels.md +13 -11
- package/package.json +1 -1
- package/src/channels/deployments/deployments-channel.ts +7 -6
- package/src/channels/deployments/index.ts +2 -3
- package/src/channels/deployments/types.ts +14 -14
- package/src/internal/cursor-deployment-relay.ts +38 -36
- package/src/internal/server.ts +13 -13
- package/dist/docs/assets/chunks/@localSearchIndexroot.BqhvUA76.js +0 -1
- package/dist/playground/assets/index-ChETdG-_.css +0 -1
- /package/dist/docs/assets/{reference_channels.md.G5GysZ7e.lean.js → reference_channels.md.CboFd5IH.lean.js} +0 -0
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
/>
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
10
10
|
<title>agent-serve playground</title>
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-JaFLlugp.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CMe7imuc.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -218,19 +218,21 @@ converge a merge-box check and sticky PR comment from default stream
|
|
|
218
218
|
events. Guide: [GitHub](../guides/github.md).
|
|
219
219
|
|
|
220
220
|
**Deployments** (`@cursor/july/channels/deployments`): pull transport
|
|
221
|
-
over `/v0/deployment-events`. Subscribe per
|
|
222
|
-
narrow with `environments` / `events`, and handle
|
|
223
|
-
`onEvent`. `
|
|
224
|
-
writer records it; the
|
|
225
|
-
case-insensitive but otherwise
|
|
221
|
+
over `/v0/deployment-events`. Subscribe per deploy source with
|
|
222
|
+
`deploySourceUris`, narrow with `environments` / `events`, and handle
|
|
223
|
+
each event in `onEvent`. `deploySourceUris` must match
|
|
224
|
+
`Deployment.deploy_source_uri` as your deployment writer records it; the
|
|
225
|
+
field has no format, and matching is case-insensitive but otherwise
|
|
226
|
+
literal. Each event carries `deploySourceUri` and `deployVersion`.
|
|
226
227
|
Author `agent/channels/deployments.ts` with `deploymentsChannel()`. The
|
|
227
228
|
serve host discovers every mounted deployments channel, runs one relay
|
|
228
|
-
for the process against the union of their
|
|
229
|
-
event to the channels that asked for it
|
|
230
|
-
|
|
231
|
-
a durable offset, so a restart resumes rather than dropping
|
|
232
|
-
empty `
|
|
233
|
-
env-configured agent stays inert until its
|
|
229
|
+
for the process against the union of their deploy sources, and routes
|
|
230
|
+
each event to the channels that asked for it; the Slack and SCM relays
|
|
231
|
+
use the same ownership. It authenticates with the host credential and
|
|
232
|
+
keeps a durable offset, so a restart resumes rather than dropping
|
|
233
|
+
events. An empty `deploySourceUris` list mounts the channel but starts
|
|
234
|
+
no relay for it, so an env-configured agent stays inert until its
|
|
235
|
+
deploy sources are set.
|
|
234
236
|
|
|
235
237
|
For other platforms like Discord or Teams, use the authored
|
|
236
238
|
`defineChannel` webhook form.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
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": {
|
|
@@ -14,11 +14,11 @@ const deploymentEventSchema = z.object({
|
|
|
14
14
|
eventType: z.enum(DEPLOYMENT_EVENT_TYPES),
|
|
15
15
|
eventId: z.string().min(1),
|
|
16
16
|
teamId: z.number(),
|
|
17
|
-
|
|
17
|
+
deploySourceUri: z.string().min(1),
|
|
18
18
|
deploymentId: z.string().min(1),
|
|
19
19
|
environment: z.string().min(1),
|
|
20
20
|
service: z.string().min(1).optional(),
|
|
21
|
-
|
|
21
|
+
deployVersion: z.string().min(1),
|
|
22
22
|
status: z.enum(["pending", "in_progress", "success", "failed"]),
|
|
23
23
|
occurredAtMs: z.number(),
|
|
24
24
|
});
|
|
@@ -60,10 +60,10 @@ export async function dispatchDeploymentEvent(input: {
|
|
|
60
60
|
* import { deploymentsChannel } from "@cursor/july/channels/deployments";
|
|
61
61
|
*
|
|
62
62
|
* export default deploymentsChannel({
|
|
63
|
-
*
|
|
63
|
+
* deploySourceUris: ["https://origin.cursor.com/repos/anysphere/everysphere"],
|
|
64
64
|
* environments: ["prod"],
|
|
65
65
|
* onEvent: async (event, { send }) => {
|
|
66
|
-
* await send(`${event.
|
|
66
|
+
* await send(`${event.deploySourceUri}@${event.deployVersion} shipped to ${event.environment}`);
|
|
67
67
|
* },
|
|
68
68
|
* });
|
|
69
69
|
* ```
|
|
@@ -81,8 +81,9 @@ export async function dispatchDeploymentEvent(input: {
|
|
|
81
81
|
export function deploymentsChannel(
|
|
82
82
|
options: DeploymentsChannelOptions
|
|
83
83
|
): ChannelDefinition {
|
|
84
|
-
// Throws here rather than at startup, so a malformed `
|
|
85
|
-
// when the channel module is authored instead of when the host
|
|
84
|
+
// Throws here rather than at startup, so a malformed `deploySourceUris`
|
|
85
|
+
// entry fails when the channel module is authored instead of when the host
|
|
86
|
+
// mounts it.
|
|
86
87
|
resolveDeploymentSubscriptions(options);
|
|
87
88
|
const routePath = options.path ?? "/";
|
|
88
89
|
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* import { deploymentsChannel } from "@cursor/july/channels/deployments";
|
|
6
6
|
*
|
|
7
7
|
* export default deploymentsChannel({
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* onEvent: (event, { send }) => send(`${event.sha} shipped`),
|
|
8
|
+
* deploySourceUris: ["https://origin.cursor.com/repos/anysphere/everysphere"],
|
|
9
|
+
* onEvent: (event, { send }) => send(`${event.deployVersion} shipped`),
|
|
11
10
|
* });
|
|
12
11
|
* ```
|
|
13
12
|
*
|
|
@@ -18,25 +18,25 @@ export interface DeploymentEvent {
|
|
|
18
18
|
eventId: string;
|
|
19
19
|
/** Owning Cursor team. */
|
|
20
20
|
teamId: number;
|
|
21
|
-
/** The Deployment's
|
|
22
|
-
|
|
21
|
+
/** The Deployment's deploy_source_uri string, case-folded. */
|
|
22
|
+
deploySourceUri: string;
|
|
23
23
|
deploymentId: string;
|
|
24
24
|
/** Environment slug, e.g. `prod`. */
|
|
25
25
|
environment: string;
|
|
26
26
|
/** Absent when the deployment records no service. */
|
|
27
27
|
service?: string;
|
|
28
|
-
|
|
28
|
+
deployVersion: string;
|
|
29
29
|
status: DeploymentStatus;
|
|
30
30
|
occurredAtMs: number;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/** What a
|
|
33
|
+
/** What a deploy-source subscription asks for. */
|
|
34
34
|
export interface DeploymentSubscription {
|
|
35
35
|
/**
|
|
36
|
-
* The
|
|
36
|
+
* The deploy-source URI as the factory records it on a Deployment. Opaque:
|
|
37
37
|
* whatever your deployment writer puts there, matched case-insensitively.
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
deploySourceUri: string;
|
|
40
40
|
/** Omit or leave empty for every environment. */
|
|
41
41
|
environments?: readonly string[];
|
|
42
42
|
/** Defaults to `deployment.succeeded`. */
|
|
@@ -45,18 +45,18 @@ export interface DeploymentSubscription {
|
|
|
45
45
|
|
|
46
46
|
export interface DeploymentsChannelOptions {
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
* `Deployment.
|
|
50
|
-
* puts there is the key — and matching is case-insensitive but
|
|
51
|
-
* literal, so a value that does not match subscribes successfully
|
|
52
|
-
* receives nothing. A bare string takes the default environment and
|
|
53
|
-
* filters.
|
|
48
|
+
* Deploy sources to watch, spelled exactly as your deployment writer records
|
|
49
|
+
* `Deployment.deploy_source_uri`. The field has no format — whatever the
|
|
50
|
+
* writer puts there is the key — and matching is case-insensitive but
|
|
51
|
+
* otherwise literal, so a value that does not match subscribes successfully
|
|
52
|
+
* and then receives nothing. A bare string takes the default environment and
|
|
53
|
+
* event filters.
|
|
54
54
|
*
|
|
55
|
-
* There is no team-wide subscription: every
|
|
55
|
+
* There is no team-wide subscription: every deploy source is named. At least
|
|
56
56
|
* one is required — an empty list is how a consumer *deregisters*, so it
|
|
57
57
|
* cannot also mean "everything".
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
deploySourceUris: readonly (string | DeploymentSubscription)[];
|
|
60
60
|
/**
|
|
61
61
|
* Applied to every subscription that does not set its own. Omit or leave
|
|
62
62
|
* empty for every environment.
|
|
@@ -22,13 +22,13 @@ const DEFAULT_EVENTS: readonly DeploymentEventType[] = ["deployment.succeeded"];
|
|
|
22
22
|
export type DeploymentRelayEvent = DeploymentEvent & { offset: string };
|
|
23
23
|
|
|
24
24
|
export interface ResolvedSubscription {
|
|
25
|
-
|
|
25
|
+
deploySourceUri: string;
|
|
26
26
|
environments: string[];
|
|
27
27
|
events: Set<DeploymentEventType>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
interface SubscriptionStatus {
|
|
31
|
-
|
|
31
|
+
deploySourceUri: string;
|
|
32
32
|
isSubscribed: boolean;
|
|
33
33
|
detail?: string;
|
|
34
34
|
}
|
|
@@ -44,22 +44,22 @@ export interface DeploymentEventRelayOptions {
|
|
|
44
44
|
pollWaitMs?: number;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/** Normalize the channel's `
|
|
47
|
+
/** Normalize the channel's `deploySourceUris` option into concrete subscriptions. */
|
|
48
48
|
export function resolveDeploymentSubscriptions(options: {
|
|
49
|
-
|
|
49
|
+
deploySourceUris: readonly (string | DeploymentSubscription)[];
|
|
50
50
|
environments?: readonly string[];
|
|
51
51
|
events?: readonly DeploymentEventType[];
|
|
52
52
|
}): ResolvedSubscription[] {
|
|
53
|
-
const
|
|
54
|
-
for (const entry of options.
|
|
53
|
+
const bySource = new Map<string, ResolvedSubscription>();
|
|
54
|
+
for (const entry of options.deploySourceUris) {
|
|
55
55
|
const subscription: DeploymentSubscription =
|
|
56
|
-
typeof entry === "string" ? {
|
|
57
|
-
// Case-folded only: `Deployment.
|
|
58
|
-
// the server keys subscriptions the same way.
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
56
|
+
typeof entry === "string" ? { deploySourceUri: entry } : entry;
|
|
57
|
+
// Case-folded only: `Deployment.deploy_source_uri` has no documented
|
|
58
|
+
// format, and the server keys subscriptions the same way.
|
|
59
|
+
const deploySourceUri = subscription.deploySourceUri.trim().toLowerCase();
|
|
60
|
+
if (deploySourceUri === "") {
|
|
61
61
|
throw new Error(
|
|
62
|
-
`deploymentsChannel: invalid
|
|
62
|
+
`deploymentsChannel: invalid deploy source "${subscription.deploySourceUri}".`
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
65
|
const environments = [
|
|
@@ -74,14 +74,14 @@ export function resolveDeploymentSubscriptions(options: {
|
|
|
74
74
|
);
|
|
75
75
|
if (events.size === 0) {
|
|
76
76
|
throw new Error(
|
|
77
|
-
`deploymentsChannel:
|
|
77
|
+
`deploymentsChannel: deploy source "${deploySourceUri}" subscribes to no events.`
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
|
-
// Later entries for the same
|
|
81
|
-
// silently replacing it.
|
|
82
|
-
const existing =
|
|
80
|
+
// Later entries for the same deploy source widen the earlier one rather
|
|
81
|
+
// than silently replacing it.
|
|
82
|
+
const existing = bySource.get(deploySourceUri);
|
|
83
83
|
if (existing === undefined) {
|
|
84
|
-
|
|
84
|
+
bySource.set(deploySourceUri, { deploySourceUri, environments, events });
|
|
85
85
|
continue;
|
|
86
86
|
}
|
|
87
87
|
for (const event of events) existing.events.add(event);
|
|
@@ -91,9 +91,10 @@ export function resolveDeploymentSubscriptions(options: {
|
|
|
91
91
|
: [...new Set([...existing.environments, ...environments])];
|
|
92
92
|
}
|
|
93
93
|
// Empty is legal here and means "this channel wants nothing", which is how
|
|
94
|
-
// an env-configured channel stays inert without a
|
|
95
|
-
// empty list means deregister, so the host simply never
|
|
96
|
-
|
|
94
|
+
// an env-configured channel stays inert without a deploy-source list. The
|
|
95
|
+
// wire-level empty list means deregister, so the host simply never
|
|
96
|
+
// registers it.
|
|
97
|
+
return [...bySource.values()];
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
/** Whether one confirmed subscription wants this event. */
|
|
@@ -136,7 +137,7 @@ export async function fanOutDeploymentEvent(input: {
|
|
|
136
137
|
const matched = input.targets.filter((target) =>
|
|
137
138
|
target.subscriptions.some(
|
|
138
139
|
(subscription) =>
|
|
139
|
-
subscription.
|
|
140
|
+
subscription.deploySourceUri === event.deploySourceUri &&
|
|
140
141
|
deploymentEventMatches(subscription, event)
|
|
141
142
|
)
|
|
142
143
|
);
|
|
@@ -154,7 +155,7 @@ export async function fanOutDeploymentEvent(input: {
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
logger(
|
|
157
|
-
`[agent-sdk] deployment-events: ${event.eventType} ${event.
|
|
158
|
+
`[agent-sdk] deployment-events: ${event.eventType} ${event.deploySourceUri}@${event.deployVersion} (${event.environment}) → ${matched.length - failures.length}/${matched.length} channel(s)`
|
|
158
159
|
);
|
|
159
160
|
if (failures.length > 0 && failures.length === matched.length) {
|
|
160
161
|
throw new Error(
|
|
@@ -166,7 +167,7 @@ export async function fanOutDeploymentEvent(input: {
|
|
|
166
167
|
export class DeploymentEventRelay {
|
|
167
168
|
private readonly core: CursorRelayCore<DeploymentRelayEvent>;
|
|
168
169
|
private subscribed = new Map<string, ResolvedSubscription>();
|
|
169
|
-
private readonly
|
|
170
|
+
private readonly loggedSkippedSources = new Set<string>();
|
|
170
171
|
|
|
171
172
|
constructor(private readonly options: DeploymentEventRelayOptions) {
|
|
172
173
|
const adapter: RelayAdapter<DeploymentRelayEvent> = {
|
|
@@ -176,7 +177,7 @@ export class DeploymentEventRelay {
|
|
|
176
177
|
buildRegistrationBody: (consumerId) => ({
|
|
177
178
|
consumerId,
|
|
178
179
|
subscriptions: options.subscriptions.map((subscription) => ({
|
|
179
|
-
|
|
180
|
+
deploySourceUri: subscription.deploySourceUri,
|
|
180
181
|
environments: subscription.environments,
|
|
181
182
|
events: [...subscription.events],
|
|
182
183
|
})),
|
|
@@ -188,23 +189,24 @@ export class DeploymentEventRelay {
|
|
|
188
189
|
subscriptions: [],
|
|
189
190
|
}),
|
|
190
191
|
applyRegistrationResponse: (json) => {
|
|
191
|
-
const result = json as {
|
|
192
|
+
const result = json as { deploySourceUris?: SubscriptionStatus[] };
|
|
192
193
|
this.subscribed = new Map();
|
|
193
|
-
this.
|
|
194
|
+
this.loggedSkippedSources.clear();
|
|
194
195
|
const lines: string[] = [];
|
|
195
196
|
const confirmed: string[] = [];
|
|
196
|
-
for (const status of result.
|
|
197
|
+
for (const status of result.deploySourceUris ?? []) {
|
|
197
198
|
const wanted = options.subscriptions.find(
|
|
198
|
-
(subscription) =>
|
|
199
|
+
(subscription) =>
|
|
200
|
+
subscription.deploySourceUri === status.deploySourceUri
|
|
199
201
|
);
|
|
200
202
|
if (!status.isSubscribed || wanted === undefined) {
|
|
201
203
|
lines.push(
|
|
202
|
-
`not subscribed to ${status.
|
|
204
|
+
`not subscribed to ${status.deploySourceUri}: ${status.detail ?? "unknown reason"}`
|
|
203
205
|
);
|
|
204
206
|
continue;
|
|
205
207
|
}
|
|
206
|
-
this.subscribed.set(status.
|
|
207
|
-
confirmed.push(status.
|
|
208
|
+
this.subscribed.set(status.deploySourceUri, wanted);
|
|
209
|
+
confirmed.push(status.deploySourceUri);
|
|
208
210
|
}
|
|
209
211
|
if (confirmed.length > 0) {
|
|
210
212
|
const kinds = [
|
|
@@ -247,16 +249,16 @@ export class DeploymentEventRelay {
|
|
|
247
249
|
if (
|
|
248
250
|
envelope === null ||
|
|
249
251
|
typeof envelope !== "object" ||
|
|
250
|
-
typeof envelope.
|
|
252
|
+
typeof envelope.deploySourceUri !== "string"
|
|
251
253
|
) {
|
|
252
254
|
return false;
|
|
253
255
|
}
|
|
254
|
-
const subscription = this.subscribed.get(envelope.
|
|
256
|
+
const subscription = this.subscribed.get(envelope.deploySourceUri);
|
|
255
257
|
if (subscription === undefined) {
|
|
256
|
-
if (!this.
|
|
257
|
-
this.
|
|
258
|
+
if (!this.loggedSkippedSources.has(envelope.deploySourceUri)) {
|
|
259
|
+
this.loggedSkippedSources.add(envelope.deploySourceUri);
|
|
258
260
|
this.options.logger(
|
|
259
|
-
`[agent-sdk] deployment-events: skipping events for ${envelope.
|
|
261
|
+
`[agent-sdk] deployment-events: skipping events for ${envelope.deploySourceUri}: not in this host's subscription (another consumer of this API key likely subscribed it)`
|
|
260
262
|
);
|
|
261
263
|
}
|
|
262
264
|
return false;
|
package/src/internal/server.ts
CHANGED
|
@@ -2291,7 +2291,7 @@ function collectCursorDeploymentTargets(
|
|
|
2291
2291
|
}
|
|
2292
2292
|
const subscriptions = resolveDeploymentSubscriptions(options);
|
|
2293
2293
|
if (subscriptions.length === 0) {
|
|
2294
|
-
// Declared the channel but named no
|
|
2294
|
+
// Declared the channel but named no deploy sources — usually an unset
|
|
2295
2295
|
// env var. Say so rather than starting a relay that receives nothing.
|
|
2296
2296
|
skipped.push(`${runtime.slug || runtime.project.name}/${channel.id}`);
|
|
2297
2297
|
continue;
|
|
@@ -2305,7 +2305,7 @@ function collectCursorDeploymentTargets(
|
|
|
2305
2305
|
/**
|
|
2306
2306
|
* Cursor deployment-events relay: automatic when a mounted agent authored
|
|
2307
2307
|
* `deploymentsChannel({ ... })`. One relay per process reads
|
|
2308
|
-
* `/v0/deployment-events` for the union of every channel's
|
|
2308
|
+
* `/v0/deployment-events` for the union of every channel's deploy sources, and
|
|
2309
2309
|
* each event is routed to the channels that asked for it — the same ownership
|
|
2310
2310
|
* `startCursorSlackRelay` and `startCursorEventRelay` have.
|
|
2311
2311
|
*/
|
|
@@ -2320,7 +2320,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2320
2320
|
const targets = collectCursorDeploymentTargets(runtimes, skipped);
|
|
2321
2321
|
if (skipped.length > 0) {
|
|
2322
2322
|
logger(
|
|
2323
|
-
`[agent-sdk] deployment-events: no
|
|
2323
|
+
`[agent-sdk] deployment-events: no deploy sources declared on ${skipped.join(", ")}; those channels receive nothing`
|
|
2324
2324
|
);
|
|
2325
2325
|
}
|
|
2326
2326
|
if (targets.length === 0) {
|
|
@@ -2328,19 +2328,19 @@ function startCursorDeploymentRelay(input: {
|
|
|
2328
2328
|
}
|
|
2329
2329
|
if (accountKey === undefined) {
|
|
2330
2330
|
throw new Error(
|
|
2331
|
-
`This project has channels subscribed to deployment events (deploymentsChannel({
|
|
2331
|
+
`This project has channels subscribed to deployment events (deploymentsChannel({ deploySourceUris })) but the host is not signed in to Cursor. ${SIGN_IN_HINT}`
|
|
2332
2332
|
);
|
|
2333
2333
|
}
|
|
2334
|
-
// Union across channels: one registration per
|
|
2335
|
-
// however many channels named it. Per-channel environment and
|
|
2336
|
-
// filtering happens at dispatch.
|
|
2337
|
-
const
|
|
2334
|
+
// Union across channels: one registration per deploy source for the whole
|
|
2335
|
+
// process, however many channels named it. Per-channel environment and
|
|
2336
|
+
// event-type filtering happens at dispatch.
|
|
2337
|
+
const bySource = new Map<string, ResolvedSubscription>();
|
|
2338
2338
|
for (const target of targets) {
|
|
2339
2339
|
for (const subscription of target.subscriptions) {
|
|
2340
|
-
const existing =
|
|
2340
|
+
const existing = bySource.get(subscription.deploySourceUri);
|
|
2341
2341
|
if (existing === undefined) {
|
|
2342
|
-
|
|
2343
|
-
|
|
2342
|
+
bySource.set(subscription.deploySourceUri, {
|
|
2343
|
+
deploySourceUri: subscription.deploySourceUri,
|
|
2344
2344
|
environments: [...subscription.environments],
|
|
2345
2345
|
events: new Set(subscription.events),
|
|
2346
2346
|
});
|
|
@@ -2369,7 +2369,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2369
2369
|
baseUrl: cursorExternalApiUrl(),
|
|
2370
2370
|
apiKey: accountKey.apiKey,
|
|
2371
2371
|
stateDir: join(baseStateRoot, "deployment-events"),
|
|
2372
|
-
subscriptions: [...
|
|
2372
|
+
subscriptions: [...bySource.values()],
|
|
2373
2373
|
logger,
|
|
2374
2374
|
dispatch: async (event) =>
|
|
2375
2375
|
fanOutDeploymentEvent({
|
|
@@ -2392,7 +2392,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2392
2392
|
});
|
|
2393
2393
|
relay.start();
|
|
2394
2394
|
logger(
|
|
2395
|
-
`[agent-sdk] deployment-events: relaying ${
|
|
2395
|
+
`[agent-sdk] deployment-events: relaying ${bySource.size} deploy-source subscription(s) to ${targets.length} channel(s)`
|
|
2396
2396
|
);
|
|
2397
2397
|
return relay;
|
|
2398
2398
|
}
|