@cursor/july 0.1.85 → 0.1.87
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
|
@@ -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-Bqn91tW4.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-C0_5hOsf.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/dist/types.d.ts
CHANGED
|
@@ -1463,8 +1463,8 @@ export interface SendMessageOptions {
|
|
|
1463
1463
|
*/
|
|
1464
1464
|
continuationToken?: string;
|
|
1465
1465
|
/**
|
|
1466
|
-
* v2 mailbox stamp from the control plane (
|
|
1467
|
-
*
|
|
1466
|
+
* v2 mailbox stamp from the control plane (hosted verified ingress or
|
|
1467
|
+
* the local CP). Channel adapters do not extract. Ignored on v1.
|
|
1468
1468
|
*/
|
|
1469
1469
|
mailboxContinuation?: MailboxContinuation;
|
|
1470
1470
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.87",
|
|
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": {
|
|
@@ -8,12 +8,11 @@ import {
|
|
|
8
8
|
POST,
|
|
9
9
|
} from "../../channels.js";
|
|
10
10
|
import {
|
|
11
|
-
bindMailboxContinuationSend,
|
|
12
11
|
continuationSource,
|
|
13
12
|
normalizeIdentity,
|
|
14
|
-
stampMailboxContinuation,
|
|
15
13
|
} from "../../internal/continuation-identity.js";
|
|
16
14
|
import { isDevMode } from "../../internal/dev-mode.js";
|
|
15
|
+
import { bindLocalIngressSend } from "../../internal/local-ingress.js";
|
|
17
16
|
import type {
|
|
18
17
|
AgentCloudOptions,
|
|
19
18
|
ArtifactsApi,
|
|
@@ -477,6 +476,10 @@ function verifyWebhookSha256Signature(args: {
|
|
|
477
476
|
* {@link handleAgentServeTrigger} fan-out. Signature verification is the
|
|
478
477
|
* caller's responsibility (HTTP route verifies; internal fan-out skips).
|
|
479
478
|
*/
|
|
479
|
+
/**
|
|
480
|
+
* Run authored GitHub hooks and `send()`. Does not extract a v2 mailbox —
|
|
481
|
+
* the local CP (or hosted verified ingress) stamps before this runs.
|
|
482
|
+
*/
|
|
480
483
|
export async function dispatchGitHubWebhook(input: {
|
|
481
484
|
options: GitHubChannelOptions;
|
|
482
485
|
eventName: string | null;
|
|
@@ -484,23 +487,7 @@ export async function dispatchGitHubWebhook(input: {
|
|
|
484
487
|
deliveryId?: string;
|
|
485
488
|
args: ChannelHandlerArgs;
|
|
486
489
|
}): Promise<Response> {
|
|
487
|
-
|
|
488
|
-
let args = input.args;
|
|
489
|
-
if (
|
|
490
|
-
continuationSource(identity, "github") === "control-plane" &&
|
|
491
|
-
input.eventName !== null
|
|
492
|
-
) {
|
|
493
|
-
const mailbox = stampMailboxContinuation(
|
|
494
|
-
identity,
|
|
495
|
-
input.eventName,
|
|
496
|
-
input.payload
|
|
497
|
-
);
|
|
498
|
-
args = {
|
|
499
|
-
...input.args,
|
|
500
|
-
send: bindMailboxContinuationSend(input.args.send, mailbox),
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
return dispatchGitHubWebhookInner({ ...input, args });
|
|
490
|
+
return dispatchGitHubWebhookInner(input);
|
|
504
491
|
}
|
|
505
492
|
|
|
506
493
|
async function dispatchGitHubWebhookInner(input: {
|
|
@@ -644,12 +631,22 @@ export async function handleGitHubWebhookRequest(input: {
|
|
|
644
631
|
return Response.json({ ok: false, error: "invalid_json" }, { status: 400 });
|
|
645
632
|
}
|
|
646
633
|
|
|
634
|
+
const eventName = input.request.headers.get("x-github-event");
|
|
635
|
+
const args =
|
|
636
|
+
eventName === null
|
|
637
|
+
? input.args
|
|
638
|
+
: bindLocalIngressSend(input.args, {
|
|
639
|
+
identity: input.options.continuation,
|
|
640
|
+
kind: "github",
|
|
641
|
+
eventType: eventName,
|
|
642
|
+
payload,
|
|
643
|
+
});
|
|
647
644
|
return dispatchGitHubWebhook({
|
|
648
645
|
options: input.options,
|
|
649
|
-
eventName
|
|
646
|
+
eventName,
|
|
650
647
|
payload,
|
|
651
648
|
deliveryId: input.request.headers.get("x-github-delivery") ?? undefined,
|
|
652
|
-
args
|
|
649
|
+
args,
|
|
653
650
|
});
|
|
654
651
|
}
|
|
655
652
|
|
|
@@ -7,12 +7,11 @@ import {
|
|
|
7
7
|
POST,
|
|
8
8
|
} from "../../channels.js";
|
|
9
9
|
import {
|
|
10
|
-
bindMailboxContinuationSend,
|
|
11
10
|
continuationSource,
|
|
12
11
|
normalizeIdentity,
|
|
13
|
-
stampMailboxContinuation,
|
|
14
12
|
} from "../../internal/continuation-identity.js";
|
|
15
13
|
import { isDevMode } from "../../internal/dev-mode.js";
|
|
14
|
+
import { bindLocalIngressSend } from "../../internal/local-ingress.js";
|
|
16
15
|
import type {
|
|
17
16
|
AgentCloudOptions,
|
|
18
17
|
AuthContext,
|
|
@@ -364,6 +363,20 @@ export function originContinuationKey(
|
|
|
364
363
|
return `origin-pr:${ctx.repository.fullName}#${ctx.pullRequestNumber}`;
|
|
365
364
|
}
|
|
366
365
|
|
|
366
|
+
/** Payload shape local/hosted CP extract expects (`fullName` + number). */
|
|
367
|
+
export function originIngressExtractPayload(
|
|
368
|
+
envelope: OriginWebhookEnvelope
|
|
369
|
+
): unknown {
|
|
370
|
+
const parsed = parseOriginPullRequestEvent({ envelope });
|
|
371
|
+
if (parsed === null) {
|
|
372
|
+
return envelope.event.payload;
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
pullRequest: { number: parsed.ctx.pullRequestNumber },
|
|
376
|
+
repository: { fullName: parsed.ctx.repository.fullName },
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
367
380
|
export function defaultOriginTitle(
|
|
368
381
|
ctx: Pick<OriginEventContext, "repository" | "pullRequestNumber">
|
|
369
382
|
): string {
|
|
@@ -532,6 +545,10 @@ async function applyDispatch(input: {
|
|
|
532
545
|
* {@link handleAgentServeTrigger} fan-out. Signature verification is the
|
|
533
546
|
* caller's responsibility (HTTP route verifies; internal fan-out skips).
|
|
534
547
|
*/
|
|
548
|
+
/**
|
|
549
|
+
* Run authored Origin hooks and `send()`. Does not extract a v2 mailbox —
|
|
550
|
+
* the local CP (or hosted verified ingress) stamps before this runs.
|
|
551
|
+
*/
|
|
535
552
|
export async function dispatchOriginWebhook(input: {
|
|
536
553
|
options: OriginChannelOptions;
|
|
537
554
|
envelope: OriginWebhookEnvelope;
|
|
@@ -545,18 +562,7 @@ export async function dispatchOriginWebhook(input: {
|
|
|
545
562
|
{ status: 400 }
|
|
546
563
|
);
|
|
547
564
|
}
|
|
548
|
-
const
|
|
549
|
-
let args = input.args;
|
|
550
|
-
if (continuationSource(identity, "origin") === "control-plane") {
|
|
551
|
-
const mailbox = stampMailboxContinuation(identity, eventType, {
|
|
552
|
-
pullRequest: { number: parsed.ctx.pullRequestNumber },
|
|
553
|
-
repository: { fullName: parsed.ctx.repository.fullName },
|
|
554
|
-
});
|
|
555
|
-
args = {
|
|
556
|
-
...input.args,
|
|
557
|
-
send: bindMailboxContinuationSend(input.args.send, mailbox),
|
|
558
|
-
};
|
|
559
|
-
}
|
|
565
|
+
const args = input.args;
|
|
560
566
|
const ctx = enrichEventContext({
|
|
561
567
|
ctx: parsed.ctx,
|
|
562
568
|
args,
|
|
@@ -711,7 +717,12 @@ export async function handleOriginWebhookRequest(input: {
|
|
|
711
717
|
return dispatchOriginWebhook({
|
|
712
718
|
options: input.options,
|
|
713
719
|
envelope,
|
|
714
|
-
args: input.args,
|
|
720
|
+
args: bindLocalIngressSend(input.args, {
|
|
721
|
+
identity: input.options.continuation,
|
|
722
|
+
kind: "origin",
|
|
723
|
+
eventType: envelope.event.type,
|
|
724
|
+
payload: originIngressExtractPayload(envelope),
|
|
725
|
+
}),
|
|
715
726
|
});
|
|
716
727
|
}
|
|
717
728
|
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
continuationSource,
|
|
8
8
|
type MailboxContinuation,
|
|
9
9
|
normalizeIdentity,
|
|
10
|
-
stampMailboxContinuation,
|
|
11
10
|
} from "../../internal/continuation-identity.js";
|
|
11
|
+
import { routeLocalIngressContinuation } from "../../internal/local-ingress.js";
|
|
12
12
|
import type {
|
|
13
13
|
ArtifactsApi,
|
|
14
14
|
HostContext,
|
|
@@ -208,12 +208,16 @@ function slackSessionContinuation(
|
|
|
208
208
|
const key = slackContinuationKey(channelId, threadTs);
|
|
209
209
|
return { probeKey: key, sendPatch: { continuationToken: key } };
|
|
210
210
|
}
|
|
211
|
-
const
|
|
212
|
-
normalizeIdentity(identity, "slack"),
|
|
213
|
-
"
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
);
|
|
211
|
+
const routed = routeLocalIngressContinuation({
|
|
212
|
+
identity: normalizeIdentity(identity, "slack"),
|
|
213
|
+
kind: "slack",
|
|
214
|
+
eventType: "message",
|
|
215
|
+
payload: { channelId, threadTs },
|
|
216
|
+
});
|
|
217
|
+
const mailbox = routed.mailboxContinuation;
|
|
218
|
+
if (mailbox === undefined) {
|
|
219
|
+
throw new Error("v2 slack ingress did not stamp a mailbox");
|
|
220
|
+
}
|
|
217
221
|
return { probeKey: mailbox.key, sendPatch: { mailboxContinuation: mailbox } };
|
|
218
222
|
}
|
|
219
223
|
|
|
@@ -361,6 +361,16 @@ export function extractContinuation(
|
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Extract primitive used by the control plane (hosted
|
|
366
|
+
* `routeVerifiedIngressContinuation` and local
|
|
367
|
+
* `routeLocalIngressContinuation`). Channel adapters must not call this.
|
|
368
|
+
*/
|
|
369
|
+
/**
|
|
370
|
+
* Extract primitive used by the control plane (hosted
|
|
371
|
+
* `routeVerifiedIngressContinuation` and local
|
|
372
|
+
* `routeLocalIngressContinuation`). Channel adapters must not call this.
|
|
373
|
+
*/
|
|
364
374
|
export function stampMailboxContinuation(
|
|
365
375
|
identity: ContinuationIdentity,
|
|
366
376
|
eventName: string,
|
|
@@ -19,10 +19,15 @@ import type { GitHubChannelOptions } from "../channels/github/types.js";
|
|
|
19
19
|
import {
|
|
20
20
|
dispatchOriginWebhook,
|
|
21
21
|
getOriginChannelOptions,
|
|
22
|
+
originIngressExtractPayload,
|
|
22
23
|
parseOriginWebhookEnvelope,
|
|
23
24
|
} from "../channels/origin/origin-channel.js";
|
|
24
25
|
import type { OriginChannelOptions } from "../channels/origin/types.js";
|
|
25
26
|
import type { ChannelHandlerArgs } from "../types.js";
|
|
27
|
+
import {
|
|
28
|
+
bindLocalIngressSend,
|
|
29
|
+
type RouteLocalIngressContinuation,
|
|
30
|
+
} from "./local-ingress.js";
|
|
26
31
|
|
|
27
32
|
export interface AgentServeGitHubTriggerTarget {
|
|
28
33
|
/** Agent mount slug (empty string in single-agent mode). */
|
|
@@ -40,6 +45,8 @@ export interface AgentServeGitHubTriggerEvent {
|
|
|
40
45
|
eventName: string | null;
|
|
41
46
|
deliveryId?: string;
|
|
42
47
|
targets: AgentServeGitHubTriggerTarget[];
|
|
48
|
+
/** Local CP ingest. Defaults to {@link routeLocalIngressContinuation}. */
|
|
49
|
+
routeContinuation?: RouteLocalIngressContinuation;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
export interface AgentServeOriginTriggerTarget {
|
|
@@ -53,6 +60,8 @@ export interface AgentServeOriginTriggerEvent {
|
|
|
53
60
|
/** Origin envelope JSON (signature already verified by the caller). */
|
|
54
61
|
bodyText: string;
|
|
55
62
|
targets: AgentServeOriginTriggerTarget[];
|
|
63
|
+
/** Local CP ingest. Defaults to {@link routeLocalIngressContinuation}. */
|
|
64
|
+
routeContinuation?: RouteLocalIngressContinuation;
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
export type AgentServeTriggerEvent =
|
|
@@ -177,12 +186,24 @@ async function handleAgentServeGitHubTrigger(
|
|
|
177
186
|
|
|
178
187
|
const targets: AgentServeTriggerTargetResult[] = [];
|
|
179
188
|
for (const target of event.targets) {
|
|
189
|
+
const args =
|
|
190
|
+
event.eventName === null
|
|
191
|
+
? target.args
|
|
192
|
+
: bindLocalIngressSend(target.args, {
|
|
193
|
+
identity: target.options.continuation,
|
|
194
|
+
kind: "github",
|
|
195
|
+
eventType: event.eventName,
|
|
196
|
+
payload,
|
|
197
|
+
...(event.routeContinuation === undefined
|
|
198
|
+
? {}
|
|
199
|
+
: { routeContinuation: event.routeContinuation }),
|
|
200
|
+
});
|
|
180
201
|
const response = await dispatchGitHubWebhook({
|
|
181
202
|
options: target.options,
|
|
182
203
|
eventName: event.eventName,
|
|
183
204
|
payload,
|
|
184
205
|
deliveryId: event.deliveryId,
|
|
185
|
-
args
|
|
206
|
+
args,
|
|
186
207
|
});
|
|
187
208
|
targets.push(await triggerTargetResult(target.slug, response));
|
|
188
209
|
}
|
|
@@ -234,7 +255,15 @@ async function handleAgentServeOriginTrigger(
|
|
|
234
255
|
const response = await dispatchOriginWebhook({
|
|
235
256
|
options: target.options,
|
|
236
257
|
envelope,
|
|
237
|
-
args: target.args,
|
|
258
|
+
args: bindLocalIngressSend(target.args, {
|
|
259
|
+
identity: target.options.continuation,
|
|
260
|
+
kind: "origin",
|
|
261
|
+
eventType: envelope.event.type,
|
|
262
|
+
payload: originIngressExtractPayload(envelope),
|
|
263
|
+
...(event.routeContinuation === undefined
|
|
264
|
+
? {}
|
|
265
|
+
: { routeContinuation: event.routeContinuation }),
|
|
266
|
+
}),
|
|
238
267
|
});
|
|
239
268
|
targets.push(await triggerTargetResult(target.slug, response));
|
|
240
269
|
}
|
|
@@ -2,15 +2,37 @@
|
|
|
2
2
|
* In-process control plane started with local `serve`. Owns schedule clocks
|
|
3
3
|
* and reminder documents + clocks, then fires into the kit the same way the
|
|
4
4
|
* Cursor backend does over HTTP when `--no-control-plane` is set.
|
|
5
|
+
*
|
|
6
|
+
* v2 ingest lives here: `routeContinuation` stamps the mailbox (July twin
|
|
7
|
+
* of hosted `routeVerifiedIngressContinuation`), and `attachScmEvents`
|
|
8
|
+
* owns the `/v0/scm-events` long poll. v1 keeps engine-side scm-events.
|
|
9
|
+
* Hosted `--no-control-plane` v2 skips that poll — the hosted CP delivers.
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
12
|
import { dirname, join } from "node:path";
|
|
8
13
|
import { type CronExpression, parseCron } from "./cron.js";
|
|
14
|
+
import {
|
|
15
|
+
type LocalIngressContinuation,
|
|
16
|
+
type RouteLocalIngressContinuationInput,
|
|
17
|
+
routeLocalIngressContinuation,
|
|
18
|
+
} from "./local-ingress.js";
|
|
9
19
|
import type { PlatformTimers } from "./platform-timers.js";
|
|
10
20
|
import { MAX_TIMER_MS } from "./reminder-schedule.js";
|
|
11
21
|
import { type ReminderRecord, ReminderStore } from "./reminder-store.js";
|
|
12
22
|
|
|
13
23
|
export interface LocalControlPlane extends PlatformTimers {
|
|
24
|
+
/**
|
|
25
|
+
* Stamp a v2 mailbox from the channel selector + live payload. Same
|
|
26
|
+
* contract as hosted verified ingress. `/v1` is a no-op.
|
|
27
|
+
*/
|
|
28
|
+
routeContinuation(
|
|
29
|
+
input: RouteLocalIngressContinuationInput
|
|
30
|
+
): LocalIngressContinuation;
|
|
31
|
+
/**
|
|
32
|
+
* Take ownership of the v2 `/v0/scm-events` long poll. Starts the relay.
|
|
33
|
+
* v1 scm-events stay on the engine serve path.
|
|
34
|
+
*/
|
|
35
|
+
attachScmEvents(relay: { start(): void; stop(): Promise<void> }): void;
|
|
14
36
|
/** Arm persisted reminder clocks after the kit fire callback is live. */
|
|
15
37
|
startClocks(): void;
|
|
16
38
|
stop(): void;
|
|
@@ -40,6 +62,7 @@ export async function createLocalControlPlane(opts: {
|
|
|
40
62
|
const reminders = new Map<string, ReminderRecord>();
|
|
41
63
|
let scheduleTimer: NodeJS.Timeout | undefined;
|
|
42
64
|
let lastFiredMinute = -1;
|
|
65
|
+
let scmRelay: { start(): void; stop(): Promise<void> } | undefined;
|
|
43
66
|
|
|
44
67
|
if (store !== undefined) {
|
|
45
68
|
for (const record of await store.list()) {
|
|
@@ -138,6 +161,12 @@ export async function createLocalControlPlane(opts: {
|
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
return {
|
|
164
|
+
routeContinuation: routeLocalIngressContinuation,
|
|
165
|
+
attachScmEvents(relay) {
|
|
166
|
+
scmRelay = relay;
|
|
167
|
+
relay.start();
|
|
168
|
+
logger("[agent-sdk] cursor-events: control-plane ingest started");
|
|
169
|
+
},
|
|
141
170
|
async upsertSchedule(input) {
|
|
142
171
|
schedules.set(input.name, parseCron(input.cron));
|
|
143
172
|
armScheduleTicker();
|
|
@@ -183,6 +212,10 @@ export async function createLocalControlPlane(opts: {
|
|
|
183
212
|
for (const id of [...reminderTimers.keys()]) {
|
|
184
213
|
clearReminderTimer(id);
|
|
185
214
|
}
|
|
215
|
+
if (scmRelay !== undefined) {
|
|
216
|
+
void scmRelay.stop();
|
|
217
|
+
scmRelay = undefined;
|
|
218
|
+
}
|
|
186
219
|
},
|
|
187
220
|
};
|
|
188
221
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local control-plane ingest for v2 continuation.
|
|
3
|
+
*
|
|
4
|
+
* Twin of backend `routeVerifiedIngressContinuation`. Hosted Cursor runs
|
|
5
|
+
* that copy after the webhook is verified. Self-hosted / `july serve`
|
|
6
|
+
* runs this copy on the in-process local CP before the channel adapter
|
|
7
|
+
* `send()`s. Channel adapters do not extract.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ChannelHandlerArgs } from "../types.js";
|
|
11
|
+
import {
|
|
12
|
+
bindMailboxContinuationSend,
|
|
13
|
+
type ContinuationIdentity,
|
|
14
|
+
continuationSource,
|
|
15
|
+
type MailboxContinuation,
|
|
16
|
+
normalizeIdentity,
|
|
17
|
+
stampMailboxContinuation,
|
|
18
|
+
} from "./continuation-identity.js";
|
|
19
|
+
|
|
20
|
+
export type LocalIngressContinuation = {
|
|
21
|
+
explicitContinuationKey?: string;
|
|
22
|
+
mailboxContinuation?: MailboxContinuation;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type RouteLocalIngressContinuation = (
|
|
26
|
+
input: RouteLocalIngressContinuationInput
|
|
27
|
+
) => LocalIngressContinuation;
|
|
28
|
+
|
|
29
|
+
export type RouteLocalIngressContinuationInput = {
|
|
30
|
+
identity?: ContinuationIdentity;
|
|
31
|
+
kind?: string;
|
|
32
|
+
eventType: string;
|
|
33
|
+
payload: unknown;
|
|
34
|
+
deterministicContinuationKey?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Selector + live payload → mailbox stamp. `/v1` and omitted identity
|
|
39
|
+
* keep the caller-supplied deterministic key and do not stamp a mailbox.
|
|
40
|
+
*/
|
|
41
|
+
export function routeLocalIngressContinuation(
|
|
42
|
+
input: RouteLocalIngressContinuationInput
|
|
43
|
+
): LocalIngressContinuation {
|
|
44
|
+
if (input.identity === undefined) {
|
|
45
|
+
return {
|
|
46
|
+
...(input.deterministicContinuationKey === undefined
|
|
47
|
+
? {}
|
|
48
|
+
: { explicitContinuationKey: input.deterministicContinuationKey }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const identity = normalizeIdentity(input.identity, input.kind);
|
|
52
|
+
if (continuationSource(identity, input.kind) !== "control-plane") {
|
|
53
|
+
return {
|
|
54
|
+
...(input.deterministicContinuationKey === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: { explicitContinuationKey: input.deterministicContinuationKey }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const mailboxContinuation = stampMailboxContinuation(
|
|
60
|
+
identity,
|
|
61
|
+
input.eventType,
|
|
62
|
+
input.payload,
|
|
63
|
+
input.kind
|
|
64
|
+
);
|
|
65
|
+
if (mailboxContinuation.key.startsWith("cont:")) {
|
|
66
|
+
return {
|
|
67
|
+
explicitContinuationKey: mailboxContinuation.key,
|
|
68
|
+
mailboxContinuation,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return { mailboxContinuation };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Bind a CP mailbox stamp onto `args.send` so the channel adapter only
|
|
76
|
+
* calls `send()` — it does not extract. No-op for `/v1`.
|
|
77
|
+
*/
|
|
78
|
+
export function bindLocalIngressSend(
|
|
79
|
+
args: ChannelHandlerArgs,
|
|
80
|
+
input: RouteLocalIngressContinuationInput & {
|
|
81
|
+
routeContinuation?: RouteLocalIngressContinuation;
|
|
82
|
+
}
|
|
83
|
+
): ChannelHandlerArgs {
|
|
84
|
+
const route = input.routeContinuation ?? routeLocalIngressContinuation;
|
|
85
|
+
const routed = route(input);
|
|
86
|
+
return {
|
|
87
|
+
...args,
|
|
88
|
+
send: bindMailboxContinuationSend(args.send, routed.mailboxContinuation),
|
|
89
|
+
};
|
|
90
|
+
}
|