@cursor/july 0.1.61 → 0.1.68
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/bin/agent-serve.js +8 -0
- package/dist/channels/github/github-channel.d.ts +1 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +14 -1
- package/dist/channels/github/types.d.ts +2 -0
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.d.ts +2 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +7 -3
- package/dist/channels/slack/dispatch.d.ts +9 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +41 -21
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +1 -0
- package/dist/channels/slack/nudge-store.d.ts +16 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -1
- package/dist/channels/slack/nudge-store.js +53 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -1
- package/dist/channels/slack/types.d.ts +22 -5
- package/dist/channels/slack/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.CW3KJh72.js → app.C5XUTCw9.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.Bk6EWpPj.js → VPLocalSearchBox.CGDTAYHY.js} +1 -1
- package/dist/docs/assets/chunks/{theme.Bz9AodBA.js → theme.lPixh250.js} +2 -2
- package/dist/docs/assets/{reference_connections.md.CSQiNzre.js → reference_connections.md.Bwswbm6H.js} +13 -3
- package/dist/docs/assets/{reference_connections.md.CSQiNzre.lean.js → reference_connections.md.Bwswbm6H.lean.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.kJfJAP2m.js → reference_tools.md.Wmvb7Lob.js} +2 -2
- package/dist/docs/assets/{reference_tools.md.kJfJAP2m.lean.js → reference_tools.md.Wmvb7Lob.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/convert-automation.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +15 -5
- 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 +5 -5
- 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/files-backends/agent-store-presigned-url.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/advertise-tools.d.ts +74 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -0
- package/dist/internal/advertise-tools.js +152 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +11 -3
- package/dist/internal/cli-manifest.d.ts +19 -0
- package/dist/internal/cli-manifest.d.ts.map +1 -0
- package/dist/internal/cli-manifest.js +49 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +9 -1
- package/dist/internal/cursor-agent-template.d.ts +9 -0
- package/dist/internal/cursor-agent-template.d.ts.map +1 -0
- package/dist/internal/cursor-agent-template.js +37 -0
- package/dist/internal/cursor-event-relay.d.ts +5 -1
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +5 -0
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +21 -3
- package/dist/internal/deploy-manifest.d.ts +85 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -0
- package/dist/internal/deploy-manifest.js +106 -0
- package/dist/internal/deployment-scm-event-stream.d.ts +113 -0
- package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -0
- package/dist/internal/deployment-scm-event-stream.js +460 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +69 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +5 -17
- package/dist/internal/mcp-host.d.ts +18 -0
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +41 -5
- package/dist/internal/mcp-oauth.d.ts +0 -4
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +0 -4
- package/dist/internal/sdk-runner.d.ts +1 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +36 -13
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +20 -2
- package/dist/internal/session-engine.d.ts +66 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +430 -110
- package/dist/internal/shallow-clone.d.ts +115 -0
- package/dist/internal/shallow-clone.d.ts.map +1 -0
- package/dist/internal/shallow-clone.js +292 -0
- package/dist/internal/slack-provision-client.d.ts +2 -0
- package/dist/internal/slack-provision-client.d.ts.map +1 -1
- package/dist/internal/slack-provision-client.js +1 -0
- package/dist/internal/turn-lease-store.d.ts +44 -0
- package/dist/internal/turn-lease-store.d.ts.map +1 -0
- package/dist/internal/turn-lease-store.js +139 -0
- package/dist/internal/workspace.d.ts +12 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +28 -0
- package/dist/playground/assets/index-CoL7xCJl.css +1 -0
- package/dist/playground/assets/index-yxs1heea.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-protocol.d.ts +122 -0
- package/dist/storage-protocol.d.ts.map +1 -0
- package/dist/storage-protocol.js +173 -0
- package/dist/storage.d.ts +2 -72
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +2 -82
- package/dist/types.d.ts +114 -4
- package/dist/types.d.ts.map +1 -1
- package/docs/reference/connections.md +80 -0
- package/docs/reference/tools.md +14 -0
- package/package.json +4 -2
- package/skills/github/SKILL.md +2 -1
- package/src/bin/agent-serve.ts +9 -0
- package/src/channels/github/github-channel.ts +18 -2
- package/src/channels/github/types.ts +2 -0
- package/src/channels/slack/channel-watch.ts +6 -3
- package/src/channels/slack/dispatch.ts +40 -3
- package/src/channels/slack/manifest.ts +1 -0
- package/src/channels/slack/nudge-store.ts +56 -0
- package/src/channels/slack/slack-channel.ts +3 -0
- package/src/channels/slack/types.ts +22 -5
- package/src/index.ts +5 -0
- package/src/internal/advertise-tools.ts +229 -0
- package/src/internal/cli-deploy.ts +22 -1
- package/src/internal/cli-manifest.ts +63 -0
- package/src/internal/cli-slack.ts +10 -1
- package/src/internal/cursor-agent-template.ts +46 -0
- package/src/internal/cursor-event-relay.ts +8 -1
- package/src/internal/deploy-client.ts +35 -6
- package/src/internal/deploy-manifest.ts +167 -0
- package/src/internal/deployment-scm-event-stream.ts +577 -0
- package/src/internal/discovery.ts +85 -0
- package/src/internal/init-project.ts +11 -20
- package/src/internal/mcp-host.ts +66 -9
- package/src/internal/mcp-oauth.ts +0 -4
- package/src/internal/sdk-runner.ts +50 -11
- package/src/internal/server.ts +21 -2
- package/src/internal/session-engine.ts +385 -19
- package/src/internal/shallow-clone.ts +407 -0
- package/src/internal/slack-provision-client.ts +3 -0
- package/src/internal/turn-lease-store.ts +147 -0
- package/src/internal/workspace.ts +27 -0
- package/src/storage-protocol.ts +247 -0
- package/src/storage.ts +16 -110
- package/src/types.ts +118 -4
- package/templates/pr-autofixer/agent/lib/drive-schemas.ts +4 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +0 -1
- package/dist/playground/assets/index-ClbuaLpm.css +0 -1
- package/dist/playground/assets/index-SqkR13k_.js +0 -85
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
SIGN_IN_HINT,
|
|
23
23
|
} from "./cursor/credentials.js";
|
|
24
24
|
import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
|
|
25
|
+
import { readDeployTemplate } from "./cursor-agent-template.js";
|
|
25
26
|
import {
|
|
26
27
|
AgentServeDeployClient,
|
|
27
28
|
type DeleteAccepted,
|
|
@@ -38,6 +39,10 @@ import {
|
|
|
38
39
|
TERMINAL_DEPLOY_STATUSES,
|
|
39
40
|
validateDeploymentSlug,
|
|
40
41
|
} from "./deploy-client.js";
|
|
42
|
+
import {
|
|
43
|
+
type AgentDeployManifestV1,
|
|
44
|
+
buildAgentDeployManifest,
|
|
45
|
+
} from "./deploy-manifest.js";
|
|
41
46
|
import { type GitRunner, inferDeployGitSource } from "./deploy-source.js";
|
|
42
47
|
import {
|
|
43
48
|
type ChildAgentDir,
|
|
@@ -661,11 +666,19 @@ async function deployOne(
|
|
|
661
666
|
...(egressAllowedDomains.length === 0 ? {} : { egressAllowedDomains }),
|
|
662
667
|
publicRoutePaths:
|
|
663
668
|
publicRoutePaths.length === 0 ? undefined : publicRoutePaths,
|
|
669
|
+
// Offline metadata extraction (Wd): the CP persists this before any VM
|
|
670
|
+
// starts. Directories without an agent project send no manifest.
|
|
671
|
+
...(manifest === undefined ? {} : { manifest: manifest.deployManifest }),
|
|
664
672
|
};
|
|
665
673
|
|
|
666
674
|
let accepted: DeployAccepted;
|
|
667
675
|
try {
|
|
668
|
-
accepted = await ctx.client.deploy({
|
|
676
|
+
accepted = await ctx.client.deploy({
|
|
677
|
+
teamId: ctx.teamId,
|
|
678
|
+
slug,
|
|
679
|
+
source,
|
|
680
|
+
template: readDeployTemplate(dir),
|
|
681
|
+
});
|
|
669
682
|
} catch (error) {
|
|
670
683
|
return renderApiError(error, ctx.err);
|
|
671
684
|
}
|
|
@@ -851,6 +864,13 @@ interface DeployManifest {
|
|
|
851
864
|
* (only `/v1/channels/…` subtrees can be public); surfaced as a warning.
|
|
852
865
|
*/
|
|
853
866
|
httpChannelPublicEndpoint: boolean;
|
|
867
|
+
/**
|
|
868
|
+
* The offline deploy manifest (Wd): derived event declarations, channel
|
|
869
|
+
* kinds, egress, routes, and the full `/v1/info` payload. Attached to the
|
|
870
|
+
* deploy request so the control plane stores metadata before any VM
|
|
871
|
+
* starts; servers that predate manifest storage strip the field.
|
|
872
|
+
*/
|
|
873
|
+
deployManifest: AgentDeployManifestV1;
|
|
854
874
|
}
|
|
855
875
|
|
|
856
876
|
/**
|
|
@@ -875,6 +895,7 @@ async function readDeployManifest(dir: string): Promise<DeployManifest> {
|
|
|
875
895
|
httpChannelPublicEndpoint: (project.httpChannel.auth ?? []).some(
|
|
876
896
|
isPublicEndpointPolicy
|
|
877
897
|
),
|
|
898
|
+
deployManifest: buildAgentDeployManifest(project),
|
|
878
899
|
};
|
|
879
900
|
}
|
|
880
901
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `july manifest` — print the offline deploy manifest (Wd-1 of
|
|
3
|
+
* `docs/agent-serve/agent-sdk-event-ingress-plan.md`).
|
|
4
|
+
*
|
|
5
|
+
* Loads the agent project from disk and emits the statically-derived
|
|
6
|
+
* metadata the control plane stores at deploy time: declared GitHub webhook
|
|
7
|
+
* events, egress domains, public routes, and the full `/v1/info` payload.
|
|
8
|
+
* Runs entirely offline — no VM, no engine, no network.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
buildAgentDeployManifest,
|
|
13
|
+
parseAgentDeployManifest,
|
|
14
|
+
} from "./deploy-manifest.js";
|
|
15
|
+
import { isAgentProjectDir, loadAgentProject } from "./discovery.js";
|
|
16
|
+
|
|
17
|
+
export interface CmdManifestOptions {
|
|
18
|
+
dir: string;
|
|
19
|
+
/** Compact single-line JSON (machine consumption); default pretty. */
|
|
20
|
+
json?: boolean;
|
|
21
|
+
/** Test seams. */
|
|
22
|
+
out?: (line: string) => void;
|
|
23
|
+
err?: (line: string) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function cmdManifest(
|
|
27
|
+
options: CmdManifestOptions
|
|
28
|
+
): Promise<number> {
|
|
29
|
+
const out = options.out ?? ((line: string) => process.stdout.write(line));
|
|
30
|
+
const err = options.err ?? ((line: string) => process.stderr.write(line));
|
|
31
|
+
|
|
32
|
+
if (!(await isAgentProjectDir(options.dir))) {
|
|
33
|
+
err(
|
|
34
|
+
`No agent project found at ${options.dir} (expected an agent.ts default-exporting defineAgent({ ... })).\n`
|
|
35
|
+
);
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const project = await loadAgentProject(options.dir);
|
|
40
|
+
for (const diagnostic of project.diagnostics) {
|
|
41
|
+
err(`${diagnostic.severity}: ${diagnostic.message}\n`);
|
|
42
|
+
}
|
|
43
|
+
if (project.diagnostics.some((d) => d.severity === "error")) {
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const manifest = buildAgentDeployManifest(project);
|
|
48
|
+
// Round-trip through the wire schema so `july manifest` can never emit
|
|
49
|
+
// something the control plane contract would reject.
|
|
50
|
+
if (
|
|
51
|
+
parseAgentDeployManifest(JSON.parse(JSON.stringify(manifest))) === undefined
|
|
52
|
+
) {
|
|
53
|
+
err("internal error: generated manifest failed schema validation\n");
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
out(
|
|
58
|
+
options.json === true
|
|
59
|
+
? `${JSON.stringify(manifest)}\n`
|
|
60
|
+
: `${JSON.stringify(manifest, null, 2)}\n`
|
|
61
|
+
);
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
@@ -402,7 +402,16 @@ function writePendingAdminWait(
|
|
|
402
402
|
? poll.oauthAuthorizeUrl
|
|
403
403
|
: undefined;
|
|
404
404
|
ctx.err(`Waiting for a Slack admin to approve the app${appSuffix}.\n\n`);
|
|
405
|
-
if (
|
|
405
|
+
if (poll.approvalRequest === "pending") {
|
|
406
|
+
// Already in the admin queue: nothing to file, so no browser hop.
|
|
407
|
+
ctx.err(
|
|
408
|
+
ctx.dim(` Approval request sent; workspace owners were notified.\n\n`)
|
|
409
|
+
);
|
|
410
|
+
if (requestUrl !== undefined) {
|
|
411
|
+
ctx.err(ctx.dim(` Send a reminder from:\n\n`));
|
|
412
|
+
ctx.err(` ${requestUrl}\n\n`);
|
|
413
|
+
}
|
|
414
|
+
} else if (requestUrl !== undefined) {
|
|
406
415
|
ctx.err(
|
|
407
416
|
ctx.dim(
|
|
408
417
|
` Slack will notify workspace owners once the request is in their queue.\n` +
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const MAX = 64;
|
|
5
|
+
const TEMPLATE_NAMES = [
|
|
6
|
+
"agentic-owners",
|
|
7
|
+
"demo",
|
|
8
|
+
"pr-autofixer",
|
|
9
|
+
"security-reviewer",
|
|
10
|
+
"triage-linear",
|
|
11
|
+
"triage-jira",
|
|
12
|
+
] as const;
|
|
13
|
+
export type TemplateName = (typeof TEMPLATE_NAMES)[number];
|
|
14
|
+
const NAMES = new Set<string>(TEMPLATE_NAMES);
|
|
15
|
+
|
|
16
|
+
export function canonicalTemplateName(name: string): string {
|
|
17
|
+
return name === "triage" ? "triage-linear" : name;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isTemplateName(name: string): name is TemplateName {
|
|
21
|
+
return NAMES.has(name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Raw scaffold marker. Validation depends on the consuming boundary. */
|
|
25
|
+
export function readCursorAgentTemplate(rootDir: string): string | undefined {
|
|
26
|
+
try {
|
|
27
|
+
const pkg = JSON.parse(
|
|
28
|
+
readFileSync(join(rootDir, "package.json"), "utf8")
|
|
29
|
+
) as {
|
|
30
|
+
cursorAgentTemplate?: unknown;
|
|
31
|
+
};
|
|
32
|
+
const stamp = pkg.cursorAgentTemplate;
|
|
33
|
+
return typeof stamp === "string" ? stamp : undefined;
|
|
34
|
+
} catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function readDeployTemplate(rootDir: string): string | undefined {
|
|
40
|
+
const stamp = readCursorAgentTemplate(rootDir);
|
|
41
|
+
if (stamp === undefined || stamp.length === 0 || stamp.length > MAX) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const name = canonicalTemplateName(stamp);
|
|
45
|
+
return isTemplateName(name) ? name : undefined;
|
|
46
|
+
}
|
|
@@ -91,7 +91,7 @@ export type CursorScmWorkflowRunMeta = CursorScmCiBaseMeta & {
|
|
|
91
91
|
workflowId?: number;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
/** One event delivered by `GET /v0/scm-events
|
|
94
|
+
/** One event delivered by `GET /v0/scm-events`. */
|
|
95
95
|
export interface CursorScmEvent {
|
|
96
96
|
offset: string;
|
|
97
97
|
provider: string;
|
|
@@ -99,6 +99,8 @@ export interface CursorScmEvent {
|
|
|
99
99
|
deliveryId?: string;
|
|
100
100
|
repo: string;
|
|
101
101
|
receivedAtMs: number;
|
|
102
|
+
/** Verified webhook body when the API stored it. */
|
|
103
|
+
payload?: unknown;
|
|
102
104
|
/** Webhook `action` when present. */
|
|
103
105
|
action?: string;
|
|
104
106
|
/** PR number when the delivery is PR-scoped. */
|
|
@@ -235,6 +237,11 @@ function readDispatchError(body: unknown): string | undefined {
|
|
|
235
237
|
return typeof error === "string" && error !== "" ? error : undefined;
|
|
236
238
|
}
|
|
237
239
|
|
|
240
|
+
/** Stored webhook body, or a metadata stub for older stream entries. */
|
|
241
|
+
export function scmEventToGitHubPayload(event: CursorScmEvent): unknown {
|
|
242
|
+
return event.payload ?? scmMetadataToGitHubPayload(event);
|
|
243
|
+
}
|
|
244
|
+
|
|
238
245
|
/**
|
|
239
246
|
* Minimal GitHub webhook body from an SCM-events metadata envelope.
|
|
240
247
|
* When `pullRequest` has title + head/base refs+shas, emits a Zod-valid
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { z } from "zod";
|
|
14
|
+
import type { AgentDeployManifestV1 } from "./deploy-manifest.js";
|
|
14
15
|
import {
|
|
15
16
|
MAX_SECRET_VALUE_BYTES,
|
|
16
17
|
MAX_SECRETS,
|
|
@@ -74,6 +75,12 @@ export type DeploySource = {
|
|
|
74
75
|
* `X-Agent-Alias-Token` (channels authored with `publicEndpoint()`).
|
|
75
76
|
*/
|
|
76
77
|
publicRoutePaths?: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Offline deploy manifest (Wd): derived declarations + `/v1/info`
|
|
80
|
+
* payload, extracted at deploy time with no VM. Optional — servers that
|
|
81
|
+
* predate manifest storage strip unknown fields.
|
|
82
|
+
*/
|
|
83
|
+
manifest?: AgentDeployManifestV1;
|
|
77
84
|
};
|
|
78
85
|
|
|
79
86
|
const SLUG_PATTERN = /^[a-z0-9_-]{1,64}$/;
|
|
@@ -422,6 +429,7 @@ export class AgentServeDeployClient {
|
|
|
422
429
|
teamId: number;
|
|
423
430
|
slug: string;
|
|
424
431
|
source: DeploySource;
|
|
432
|
+
template?: string;
|
|
425
433
|
}): Promise<DeployAccepted> {
|
|
426
434
|
const slugError = validateDeploymentSlug(args.slug);
|
|
427
435
|
if (slugError !== undefined) {
|
|
@@ -453,13 +461,34 @@ export class AgentServeDeployClient {
|
|
|
453
461
|
: { egressAllowedDomains: args.source.egressAllowedDomains }),
|
|
454
462
|
// JSON.stringify drops undefined keys.
|
|
455
463
|
publicRoutePaths: args.source.publicRoutePaths,
|
|
464
|
+
...(args.source.manifest === undefined
|
|
465
|
+
? {}
|
|
466
|
+
: { manifest: args.source.manifest }),
|
|
467
|
+
template: args.template,
|
|
456
468
|
};
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
469
|
+
const send = (): Promise<unknown> =>
|
|
470
|
+
this.request({
|
|
471
|
+
method: "POST",
|
|
472
|
+
path: "/internal/agent-serve/deployments",
|
|
473
|
+
body,
|
|
474
|
+
context: { verb: "deploy", slug: args.slug },
|
|
475
|
+
});
|
|
476
|
+
let raw: unknown;
|
|
477
|
+
try {
|
|
478
|
+
raw = await send();
|
|
479
|
+
} catch (error) {
|
|
480
|
+
const unsupportedTemplate =
|
|
481
|
+
args.template !== undefined &&
|
|
482
|
+
error instanceof DeployApiError &&
|
|
483
|
+
error.status === 400 &&
|
|
484
|
+
error.message.toLowerCase().includes("template") &&
|
|
485
|
+
/unrecognized|unknown/i.test(error.message);
|
|
486
|
+
if (!unsupportedTemplate) {
|
|
487
|
+
throw error;
|
|
488
|
+
}
|
|
489
|
+
delete body.template;
|
|
490
|
+
raw = await send();
|
|
491
|
+
}
|
|
463
492
|
return deployAcceptedSchema.parse(raw);
|
|
464
493
|
}
|
|
465
494
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline deploy manifest (workstream Wd of
|
|
3
|
+
* `docs/agent-serve/agent-sdk-event-ingress-plan.md`, Wd-0/Wd-1).
|
|
4
|
+
*
|
|
5
|
+
* The manifest is every piece of deployment metadata that is statically
|
|
6
|
+
* derivable from the agent project — extracted at build/deploy time, with no
|
|
7
|
+
* VM, no engine, and no network — so the control plane can store it before
|
|
8
|
+
* any pod starts and keep answering metadata questions while a deployment is
|
|
9
|
+
* offline or hibernated.
|
|
10
|
+
*
|
|
11
|
+
* Two layers with different stability contracts:
|
|
12
|
+
*
|
|
13
|
+
* - `declarations` is the **versioned wire contract** consumed by the
|
|
14
|
+
* control plane (Wd-2): GitHub webhook events the mounted channels can
|
|
15
|
+
* dispatch on, egress domains, and public route paths. Validated by
|
|
16
|
+
* {@link agentDeployManifestV1Schema}; the CP stores these as columns /
|
|
17
|
+
* column views. Repos are deliberately NOT here — which repos a
|
|
18
|
+
* deployment watches is per-instance deploy configuration, not a property
|
|
19
|
+
* of the code.
|
|
20
|
+
* - `info` is the full static project description — the same payload the
|
|
21
|
+
* engine serves at `GET /v1/info` (`projectInfo`). Stored opaquely by the
|
|
22
|
+
* CP so `/v1/info`-class reads can be served for offline deployments
|
|
23
|
+
* (Wd-5). Its shape evolves with the SDK and is intentionally not pinned
|
|
24
|
+
* by the wire schema beyond "is an object".
|
|
25
|
+
*
|
|
26
|
+
* Event derivation note: each mounted `githubChannel()` publishes its
|
|
27
|
+
* derived event set on `ChannelMeta.githubWebhookEvents` (computed by
|
|
28
|
+
* `deriveGitHubWebhookEvents` from the declared hooks). The manifest is the
|
|
29
|
+
* union across mounted channels; a project with no GitHub channel gets no
|
|
30
|
+
* `githubWebhookEvents` key at all — such a deployment subscribes to
|
|
31
|
+
* nothing, regardless of the repos configured at deploy time.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { z } from "zod";
|
|
35
|
+
import { isPublicEndpointPolicy } from "../channels.js";
|
|
36
|
+
import { isCursorHostedStorage } from "../storage-backends/cursor-hosted.js";
|
|
37
|
+
import type { AgentProject, AgentProjectInfo } from "../types.js";
|
|
38
|
+
import { projectInfo } from "./discovery.js";
|
|
39
|
+
import { packageVersion } from "./distribution.js";
|
|
40
|
+
|
|
41
|
+
export const DEPLOY_MANIFEST_VERSION: 1 = 1;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Versioned wire contract for the CP-consumed declaration fields. Kept
|
|
45
|
+
* module-private (the package builds with isolated declarations); consumers
|
|
46
|
+
* use {@link parseAgentDeployManifest} and the explicit interfaces below.
|
|
47
|
+
*/
|
|
48
|
+
const agentDeployManifestV1Schema = z.object({
|
|
49
|
+
manifestVersion: z.literal(DEPLOY_MANIFEST_VERSION),
|
|
50
|
+
generatedAtMs: z.number().int().nonnegative(),
|
|
51
|
+
cliVersion: z.string().min(1),
|
|
52
|
+
declarations: z.object({
|
|
53
|
+
githubWebhookEvents: z.array(z.string().min(1).max(64)).max(64).optional(),
|
|
54
|
+
egressDomains: z.array(z.string().min(1)).max(64),
|
|
55
|
+
publicRoutePaths: z.array(z.string().min(1)).max(64),
|
|
56
|
+
frameworkStorage: z.enum(["cursor-hosted", "other", "none"]).optional(),
|
|
57
|
+
channels: z
|
|
58
|
+
.array(
|
|
59
|
+
z.object({
|
|
60
|
+
id: z.string().min(1).max(128),
|
|
61
|
+
kind: z.string().min(1).max(32).optional(),
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
.max(64),
|
|
65
|
+
}),
|
|
66
|
+
// Full `projectInfo` payload — opaque to the wire contract.
|
|
67
|
+
info: z.record(z.unknown()),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export interface AgentDeployManifestDeclarations {
|
|
71
|
+
/**
|
|
72
|
+
* Union of every mounted GitHub channel's derived webhook events.
|
|
73
|
+
* Absent (not empty) when the project mounts no GitHub channel.
|
|
74
|
+
*/
|
|
75
|
+
githubWebhookEvents?: string[];
|
|
76
|
+
egressDomains: string[];
|
|
77
|
+
publicRoutePaths: string[];
|
|
78
|
+
/** Where this runtime mirrors framework-owned sessions and event chunks. */
|
|
79
|
+
frameworkStorage?: "cursor-hosted" | "other" | "none";
|
|
80
|
+
/**
|
|
81
|
+
* Mounted channels with their kinds. The control plane derives the
|
|
82
|
+
* continuation identity (session key) for **defined** kinds it knows
|
|
83
|
+
* ("github", "slack"); a custom channel with no kind gets per-deployment
|
|
84
|
+
* serialization until a declared continuation config exists (a future
|
|
85
|
+
* schema field — reserved, deliberately not implemented yet). This is
|
|
86
|
+
* what lets the CP compute workflow/session identity before any engine
|
|
87
|
+
* runs.
|
|
88
|
+
*/
|
|
89
|
+
channels: Array<{ id: string; kind?: string }>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface AgentDeployManifestV1 {
|
|
93
|
+
manifestVersion: 1;
|
|
94
|
+
/** Unix ms at extraction. */
|
|
95
|
+
generatedAtMs: number;
|
|
96
|
+
/** `@cursor/july` version that produced the manifest. */
|
|
97
|
+
cliVersion: string;
|
|
98
|
+
declarations: AgentDeployManifestDeclarations;
|
|
99
|
+
/**
|
|
100
|
+
* Full `projectInfo` payload (see `GET /v1/info`). Opaque to the wire
|
|
101
|
+
* contract; the CP stores it verbatim.
|
|
102
|
+
*/
|
|
103
|
+
info: AgentProjectInfo;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Extract the offline deploy manifest from a loaded project. Pure: no
|
|
108
|
+
* network, no filesystem beyond what `loadAgentProject` already read.
|
|
109
|
+
*/
|
|
110
|
+
export function buildAgentDeployManifest(
|
|
111
|
+
project: AgentProject,
|
|
112
|
+
options?: { nowMs?: number; cliVersion?: string }
|
|
113
|
+
): AgentDeployManifestV1 {
|
|
114
|
+
const events = new Set<string>();
|
|
115
|
+
for (const channel of project.channels) {
|
|
116
|
+
for (const event of channel.definition.meta?.githubWebhookEvents ?? []) {
|
|
117
|
+
events.add(event);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Same mapping as `july deploy` (cli-deploy.ts): channels declared public
|
|
122
|
+
// via `publicEndpoint()` become alias-token-free route prefixes.
|
|
123
|
+
const publicRoutePaths = project.channels
|
|
124
|
+
.filter((channel) =>
|
|
125
|
+
(channel.definition.auth ?? []).some(isPublicEndpointPolicy)
|
|
126
|
+
)
|
|
127
|
+
.map((channel) => `/v1/channels/${channel.id}`);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
manifestVersion: DEPLOY_MANIFEST_VERSION,
|
|
131
|
+
generatedAtMs: options?.nowMs ?? Date.now(),
|
|
132
|
+
cliVersion: options?.cliVersion ?? packageVersion(),
|
|
133
|
+
declarations: {
|
|
134
|
+
...(events.size === 0 ? {} : { githubWebhookEvents: [...events].sort() }),
|
|
135
|
+
egressDomains: [...(project.agent.hosting?.egressDomains ?? [])],
|
|
136
|
+
publicRoutePaths,
|
|
137
|
+
frameworkStorage:
|
|
138
|
+
project.storage === undefined
|
|
139
|
+
? "none"
|
|
140
|
+
: isCursorHostedStorage(project.storage)
|
|
141
|
+
? "cursor-hosted"
|
|
142
|
+
: "other",
|
|
143
|
+
channels: project.channels.map((channel) => ({
|
|
144
|
+
id: channel.id,
|
|
145
|
+
...(channel.definition.meta?.kind === undefined
|
|
146
|
+
? {}
|
|
147
|
+
: { kind: channel.definition.meta.kind }),
|
|
148
|
+
})),
|
|
149
|
+
},
|
|
150
|
+
info: projectInfo(project),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Parse an untrusted manifest (round-trip check for the CLI's own output;
|
|
156
|
+
* the CP re-validates server-side with its own copy of the contract).
|
|
157
|
+
*/
|
|
158
|
+
export function parseAgentDeployManifest(
|
|
159
|
+
raw: unknown
|
|
160
|
+
): AgentDeployManifestV1 | undefined {
|
|
161
|
+
const parsed = agentDeployManifestV1Schema.safeParse(raw);
|
|
162
|
+
// `info` is opaque to the wire schema (validated as a plain object) but
|
|
163
|
+
// typed as AgentProjectInfo for producers/consumers inside the SDK.
|
|
164
|
+
return parsed.success
|
|
165
|
+
? (parsed.data as unknown as AgentDeployManifestV1)
|
|
166
|
+
: undefined;
|
|
167
|
+
}
|