@cursor/july 0.1.62 → 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/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.B-biogkN.js → app.C5XUTCw9.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DZqKtFZS.js → VPLocalSearchBox.CGDTAYHY.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BwC5zP2r.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/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/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 +18 -0
- 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 +113 -3
- 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/src/bin/agent-serve.ts +9 -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/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/sdk-runner.ts +50 -11
- package/src/internal/server.ts +19 -0
- 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 +117 -3
- package/templates/pr-autofixer/agent/lib/drive-schemas.ts +4 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.5ozYNf74.js +0 -1
- package/dist/playground/assets/index-B4rcOgeP.css +0 -1
- package/dist/playground/assets/index-DXcY42Jv.js +0 -85
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment-scoped SCM event intake for hosted multi-tenant agents.
|
|
3
|
+
*
|
|
4
|
+
* Instead of receiving HMAC-signed webhook forwards from the backend (which
|
|
5
|
+
* required a shared `GITHUB_WEBHOOK_SECRET` / forward-secret pair), the
|
|
6
|
+
* engine pulls its deliveries from the control plane's deployment-scoped
|
|
7
|
+
* event stream:
|
|
8
|
+
*
|
|
9
|
+
* GET /internal/agent-serve/automation-agent-sdk/events
|
|
10
|
+
*
|
|
11
|
+
* authenticated with the engine pod's `CURSOR_API_KEY` — the same
|
|
12
|
+
* credential (and server-side deployment allowlist) that authorizes the
|
|
13
|
+
* consuming agent's customer list read. The backend writes each envelope's
|
|
14
|
+
* `installationId` itself from the verified GitHub delivery, so agent-side
|
|
15
|
+
* tenant binding does not depend on an HMAC-covered payload.
|
|
16
|
+
*
|
|
17
|
+
* Each envelope is stubbed into a GitHub-shaped `pull_request` webhook body
|
|
18
|
+
* and POSTed to this process's own github channel route over loopback
|
|
19
|
+
* (admitted by the channel's no-secret `localDevStrict` policy), so the
|
|
20
|
+
* consuming agent's whole pipeline — Zod parse, cursorAccount credential
|
|
21
|
+
* lease, customer routing, tools — runs unchanged. Loopback (rather than
|
|
22
|
+
* the in-process `handleAgentServeTrigger` fan-out the `--cursor-events`
|
|
23
|
+
* relay uses) is deliberate: this stream is started from a channel's
|
|
24
|
+
* `onStart`, which cannot reach the serve-level mount/handler wiring.
|
|
25
|
+
*
|
|
26
|
+
* Offsets are client-owned (`<ms>-<seq>` Redis stream ids) and persisted
|
|
27
|
+
* under the state dir so a restart resumes where it left off instead of
|
|
28
|
+
* dropping deliveries (the old push path was fire-and-forget).
|
|
29
|
+
*
|
|
30
|
+
* INTERNAL: first-party host plumbing shared by agents in `factory/*`.
|
|
31
|
+
* Consumed via relative source imports, following
|
|
32
|
+
* `tenant-mcp/run-mcp-registry.ts`. Policy (which deliveries to act on,
|
|
33
|
+
* tenant routing) stays in the consuming agent; this module is transport
|
|
34
|
+
* only.
|
|
35
|
+
*/
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
// Node builtins only: hosted engines install the consuming agent's directory
|
|
46
|
+
// alone and load this file as raw TypeScript source, so third-party imports
|
|
47
|
+
// (even zod) do not resolve from here. See the tenant-mcp internals for the
|
|
48
|
+
// same constraint.
|
|
49
|
+
import { createHmac } from "node:crypto";
|
|
50
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
51
|
+
import { join } from "node:path";
|
|
52
|
+
const DEFAULT_BASE_URL = "https://api.cursor.com";
|
|
53
|
+
const EVENTS_PATH = "/internal/agent-serve/automation-agent-sdk/events";
|
|
54
|
+
const POLL_WAIT_MS = 20000;
|
|
55
|
+
const POLL_LIMIT = 100;
|
|
56
|
+
const MIN_BACKOFF_MS = 1000;
|
|
57
|
+
const MAX_BACKOFF_MS = 30000;
|
|
58
|
+
// Bounded self-dispatch retry before the offset advances past a delivery
|
|
59
|
+
// (mirrors CursorRelayCore): transient loopback failures (connection
|
|
60
|
+
// reset, channel briefly unavailable at startup) get a few tries; a
|
|
61
|
+
// poison delivery still costs only DISPATCH_ATTEMPTS and is then skipped.
|
|
62
|
+
const DISPATCH_ATTEMPTS = 3;
|
|
63
|
+
const DISPATCH_RETRY_DELAYS_MS = [250, 1000];
|
|
64
|
+
/** Auth/gate failures (401/403/404) are config problems; poll slowly. */
|
|
65
|
+
const AUTH_FAILURE_BACKOFF_MS = 60000;
|
|
66
|
+
const OFFSET_SNAPSHOT_VERSION = 1;
|
|
67
|
+
function isRecord(value) {
|
|
68
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69
|
+
}
|
|
70
|
+
function nonEmptyString(value) {
|
|
71
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
72
|
+
}
|
|
73
|
+
function optionalString(value) {
|
|
74
|
+
return value === undefined || typeof value === "string";
|
|
75
|
+
}
|
|
76
|
+
function optionalBoolean(value) {
|
|
77
|
+
return value === undefined || typeof value === "boolean";
|
|
78
|
+
}
|
|
79
|
+
function parsePullRequestMeta(value) {
|
|
80
|
+
if (!isRecord(value)) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const number = value.number;
|
|
84
|
+
const url = nonEmptyString(value.url);
|
|
85
|
+
const headRef = nonEmptyString(value.headRef);
|
|
86
|
+
const headSha = nonEmptyString(value.headSha);
|
|
87
|
+
const baseRef = nonEmptyString(value.baseRef);
|
|
88
|
+
if (typeof number !== "number" ||
|
|
89
|
+
!Number.isInteger(number) ||
|
|
90
|
+
number <= 0 ||
|
|
91
|
+
url === undefined ||
|
|
92
|
+
typeof value.title !== "string" ||
|
|
93
|
+
!(value.body === undefined ||
|
|
94
|
+
value.body === null ||
|
|
95
|
+
typeof value.body === "string") ||
|
|
96
|
+
!optionalBoolean(value.draft) ||
|
|
97
|
+
!optionalBoolean(value.merged) ||
|
|
98
|
+
!optionalString(value.authorLogin) ||
|
|
99
|
+
headRef === undefined ||
|
|
100
|
+
headSha === undefined ||
|
|
101
|
+
baseRef === undefined ||
|
|
102
|
+
!optionalString(value.baseSha)) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
number,
|
|
107
|
+
url,
|
|
108
|
+
title: value.title,
|
|
109
|
+
body: value.body,
|
|
110
|
+
draft: value.draft,
|
|
111
|
+
merged: value.merged,
|
|
112
|
+
authorLogin: value.authorLogin,
|
|
113
|
+
headRef,
|
|
114
|
+
headSha,
|
|
115
|
+
baseRef,
|
|
116
|
+
baseSha: value.baseSha,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Unknown envelope shapes return undefined (skipped, forward compat). */
|
|
120
|
+
export function parseAgentSdkScmEventEnvelope(value) {
|
|
121
|
+
if (!isRecord(value) || value.provider !== "github") {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const eventName = nonEmptyString(value.eventName);
|
|
125
|
+
const deliveryId = nonEmptyString(value.deliveryId);
|
|
126
|
+
const repo = nonEmptyString(value.repo);
|
|
127
|
+
const action = nonEmptyString(value.action);
|
|
128
|
+
const installationId = value.installationId;
|
|
129
|
+
const pullRequest = parsePullRequestMeta(value.pullRequest);
|
|
130
|
+
if (eventName === undefined ||
|
|
131
|
+
deliveryId === undefined ||
|
|
132
|
+
repo === undefined ||
|
|
133
|
+
typeof value.receivedAtMs !== "number" ||
|
|
134
|
+
action === undefined ||
|
|
135
|
+
typeof installationId !== "number" ||
|
|
136
|
+
!Number.isInteger(installationId) ||
|
|
137
|
+
installationId <= 0 ||
|
|
138
|
+
pullRequest === undefined ||
|
|
139
|
+
!(value.occurredAtMs === undefined || typeof value.occurredAtMs === "number")) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
provider: "github",
|
|
144
|
+
eventName,
|
|
145
|
+
deliveryId,
|
|
146
|
+
repo,
|
|
147
|
+
receivedAtMs: value.receivedAtMs,
|
|
148
|
+
action,
|
|
149
|
+
installationId,
|
|
150
|
+
pullRequest,
|
|
151
|
+
occurredAtMs: value.occurredAtMs,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function parsePollResponse(value) {
|
|
155
|
+
if (!isRecord(value) || !Array.isArray(value.events)) {
|
|
156
|
+
throw new Error("malformed poll response");
|
|
157
|
+
}
|
|
158
|
+
const events = [];
|
|
159
|
+
for (const entry of value.events) {
|
|
160
|
+
if (!isRecord(entry)) {
|
|
161
|
+
throw new Error("malformed poll response event");
|
|
162
|
+
}
|
|
163
|
+
const offset = nonEmptyString(entry.offset);
|
|
164
|
+
if (offset === undefined) {
|
|
165
|
+
throw new Error("malformed poll response event offset");
|
|
166
|
+
}
|
|
167
|
+
events.push({ offset, envelope: entry.envelope });
|
|
168
|
+
}
|
|
169
|
+
if (typeof value.nextOffset !== "string" ||
|
|
170
|
+
typeof value.gapDetected !== "boolean") {
|
|
171
|
+
throw new Error("malformed poll response fields");
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
events,
|
|
175
|
+
nextOffset: value.nextOffset,
|
|
176
|
+
gapDetected: value.gapDetected,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Stub a GitHub-shaped `pull_request` webhook body from an envelope —
|
|
181
|
+
* enough for agent-serve's pull_request Zod schema, plus `installation.id`
|
|
182
|
+
* so the channel's `ctx.installationId` tenant binding keeps working.
|
|
183
|
+
* Pure — exported for tests.
|
|
184
|
+
*/
|
|
185
|
+
export function envelopeToGitHubPayload(envelope) {
|
|
186
|
+
const slash = envelope.repo.indexOf("/");
|
|
187
|
+
const owner = slash === -1 ? "" : envelope.repo.slice(0, slash);
|
|
188
|
+
const name = slash === -1 ? envelope.repo : envelope.repo.slice(slash + 1);
|
|
189
|
+
const pr = envelope.pullRequest;
|
|
190
|
+
return {
|
|
191
|
+
action: envelope.action,
|
|
192
|
+
repository: {
|
|
193
|
+
full_name: envelope.repo,
|
|
194
|
+
name,
|
|
195
|
+
html_url: `https://github.com/${envelope.repo}`,
|
|
196
|
+
owner: { login: owner },
|
|
197
|
+
},
|
|
198
|
+
pull_request: {
|
|
199
|
+
number: pr.number,
|
|
200
|
+
html_url: pr.url,
|
|
201
|
+
title: pr.title,
|
|
202
|
+
body: pr.body,
|
|
203
|
+
draft: pr.draft,
|
|
204
|
+
merged: pr.merged,
|
|
205
|
+
user: pr.authorLogin === undefined ? undefined : { login: pr.authorLogin },
|
|
206
|
+
head: { ref: pr.headRef, sha: pr.headSha },
|
|
207
|
+
base: { ref: pr.baseRef, sha: pr.baseSha },
|
|
208
|
+
},
|
|
209
|
+
installation: { id: envelope.installationId },
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/** Durable offset snapshot under `<stateDir>/offset.json`. */
|
|
213
|
+
class OffsetStore {
|
|
214
|
+
constructor(stateDir) {
|
|
215
|
+
this.stateDir = stateDir;
|
|
216
|
+
}
|
|
217
|
+
get filePath() {
|
|
218
|
+
return join(this.stateDir, "offset.json");
|
|
219
|
+
}
|
|
220
|
+
load() {
|
|
221
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
222
|
+
try {
|
|
223
|
+
const raw = yield readFile(this.filePath, "utf8");
|
|
224
|
+
const parsed = JSON.parse(raw);
|
|
225
|
+
if (parsed.version === OFFSET_SNAPSHOT_VERSION &&
|
|
226
|
+
typeof parsed.offset === "string" &&
|
|
227
|
+
parsed.offset !== "") {
|
|
228
|
+
return parsed.offset;
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
catch (_a) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
save(offset) {
|
|
238
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
239
|
+
yield mkdir(this.stateDir, { recursive: true });
|
|
240
|
+
// Write-then-rename so a crash mid-write never corrupts the snapshot.
|
|
241
|
+
const tmpPath = `${this.filePath}.tmp`;
|
|
242
|
+
yield writeFile(tmpPath, JSON.stringify({
|
|
243
|
+
version: OFFSET_SNAPSHOT_VERSION,
|
|
244
|
+
offset,
|
|
245
|
+
updatedAt: new Date().toISOString(),
|
|
246
|
+
}, null, 2), "utf8");
|
|
247
|
+
yield rename(tmpPath, this.filePath);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Port the serve process is actually listening on, read from its own argv.
|
|
253
|
+
*
|
|
254
|
+
* The channel runs inside the serve process, so `--port <n>` / `--port=<n>`
|
|
255
|
+
* on the command line is authoritative. Hosted-platform engines start with
|
|
256
|
+
* `--port 26100` and export no `PORT` env, so guessing a fixed default posts
|
|
257
|
+
* self-dispatches at a port nothing listens on ("fetch failed" for every
|
|
258
|
+
* delivery).
|
|
259
|
+
*/
|
|
260
|
+
export function servePortFromArgv(argv) {
|
|
261
|
+
var _a;
|
|
262
|
+
const flagIndex = argv.lastIndexOf("--port");
|
|
263
|
+
if (flagIndex !== -1) {
|
|
264
|
+
const value = (_a = argv[flagIndex + 1]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
265
|
+
if (value !== undefined && /^\d+$/.test(value)) {
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (let i = argv.length - 1; i >= 0; i--) {
|
|
270
|
+
const arg = argv[i];
|
|
271
|
+
if (arg !== undefined && arg.startsWith("--port=")) {
|
|
272
|
+
const value = arg.slice("--port=".length).trim();
|
|
273
|
+
if (/^\d+$/.test(value)) {
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
function delay(ms, signal) {
|
|
281
|
+
return new Promise((resolve) => {
|
|
282
|
+
var _a;
|
|
283
|
+
const timer = setTimeout(finish, ms);
|
|
284
|
+
(_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
|
|
285
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
286
|
+
function finish() {
|
|
287
|
+
clearTimeout(timer);
|
|
288
|
+
signal.removeEventListener("abort", finish);
|
|
289
|
+
resolve();
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Start the pull loop. Returns undefined (with a log line) when required
|
|
295
|
+
* config is missing so `serve` still starts — mirroring how other relays
|
|
296
|
+
* degrade instead of failing the host.
|
|
297
|
+
*/
|
|
298
|
+
export function startAgentSdkEventStream(options = {}) {
|
|
299
|
+
var _a, _b, _c, _d, _e;
|
|
300
|
+
var _f, _g, _h, _j, _k, _l, _m, _o;
|
|
301
|
+
const env = (_f = options.env) !== null && _f !== void 0 ? _f : process.env;
|
|
302
|
+
const logLabel = (_g = options.logLabel) !== null && _g !== void 0 ? _g : "agent-sdk/deployment-events";
|
|
303
|
+
const rawLog = (_h = options.logger) !== null && _h !== void 0 ? _h : (() => { });
|
|
304
|
+
const log = (message) => {
|
|
305
|
+
rawLog(`[${logLabel}] ${message}`);
|
|
306
|
+
};
|
|
307
|
+
const doFetch = (_j = options.fetch) !== null && _j !== void 0 ? _j : fetch;
|
|
308
|
+
const envPrefix = (_k = options.envPrefix) !== null && _k !== void 0 ? _k : "AGENT_SERVE";
|
|
309
|
+
const apiKey = (_a = env.CURSOR_API_KEY) === null || _a === void 0 ? void 0 : _a.trim();
|
|
310
|
+
if (apiKey === undefined || apiKey === "") {
|
|
311
|
+
log("CURSOR_API_KEY missing; deployment event stream disabled");
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
const baseRaw = ((_l = env.CURSOR_API_BASE_URL) !== null && _l !== void 0 ? _l : DEFAULT_BASE_URL).trim();
|
|
315
|
+
const base = baseRaw.endsWith("/") ? baseRaw.slice(0, -1) : baseRaw;
|
|
316
|
+
// Loopback self-dispatch into this process's github channel route, single
|
|
317
|
+
// agent mounting assumed (multi-agent mounts need the slugged path via the
|
|
318
|
+
// env var override). Port resolution: explicit env override, then `PORT`,
|
|
319
|
+
// then the serve process's own `--port` argv (hosted-platform engines run
|
|
320
|
+
// `serve --port 26100` and export no PORT), then the :3000 default of the
|
|
321
|
+
// corp hosted image.
|
|
322
|
+
const selfUrl = ((_b = env[`${envPrefix}_SELF_WEBHOOK_URL`]) === null || _b === void 0 ? void 0 : _b.trim()) ||
|
|
323
|
+
`http://127.0.0.1:${((_c = env.PORT) === null || _c === void 0 ? void 0 : _c.trim()) ||
|
|
324
|
+
servePortFromArgv((_m = options.argv) !== null && _m !== void 0 ? _m : process.argv) ||
|
|
325
|
+
"3000"}/v1/channels/github`;
|
|
326
|
+
log(`self-dispatch url: ${selfUrl}`);
|
|
327
|
+
const stateDir = ((_d = env[`${envPrefix}_EVENTS_STATE_DIR`]) === null || _d === void 0 ? void 0 : _d.trim()) ||
|
|
328
|
+
join(process.cwd(), ".agent-serve", (_o = options.stateDirName) !== null && _o !== void 0 ? _o : "deployment-events");
|
|
329
|
+
const offsetStore = new OffsetStore(stateDir);
|
|
330
|
+
const abort = new AbortController();
|
|
331
|
+
// Transition aid: while the deployment still sets GITHUB_WEBHOOK_SECRET,
|
|
332
|
+
// the channel's auth is HMAC-only (allowAll + signature check), so sign
|
|
333
|
+
// the self-dispatch with it. Once the secret is removed from the env,
|
|
334
|
+
// the channel falls back to its loopback policy and the unsigned POST is
|
|
335
|
+
// admitted — either configuration works, so rollout order doesn't matter.
|
|
336
|
+
const webhookSecret = (_e = env.GITHUB_WEBHOOK_SECRET) === null || _e === void 0 ? void 0 : _e.trim();
|
|
337
|
+
function dispatch(envelope) {
|
|
338
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
339
|
+
var _a;
|
|
340
|
+
const body = JSON.stringify(envelopeToGitHubPayload(envelope));
|
|
341
|
+
const headers = {
|
|
342
|
+
"content-type": "application/json",
|
|
343
|
+
"x-github-event": envelope.eventName,
|
|
344
|
+
"x-github-delivery": envelope.deliveryId,
|
|
345
|
+
};
|
|
346
|
+
if (webhookSecret !== undefined && webhookSecret !== "") {
|
|
347
|
+
const signature = createHmac("sha256", webhookSecret)
|
|
348
|
+
.update(body)
|
|
349
|
+
.digest("hex");
|
|
350
|
+
headers["x-hub-signature-256"] = `sha256=${signature}`;
|
|
351
|
+
}
|
|
352
|
+
const response = yield doFetch(selfUrl, {
|
|
353
|
+
method: "POST",
|
|
354
|
+
headers,
|
|
355
|
+
body,
|
|
356
|
+
signal: AbortSignal.timeout(POLL_WAIT_MS),
|
|
357
|
+
});
|
|
358
|
+
void ((_a = response.body) === null || _a === void 0 ? void 0 : _a.cancel().catch(() => { }));
|
|
359
|
+
if (!response.ok) {
|
|
360
|
+
throw new Error(`self dispatch returned ${response.status}`);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function run() {
|
|
365
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
366
|
+
var _a;
|
|
367
|
+
var _b;
|
|
368
|
+
let after = (_b = (yield offsetStore.load())) !== null && _b !== void 0 ? _b : "$";
|
|
369
|
+
let backoffMs = MIN_BACKOFF_MS;
|
|
370
|
+
log(`pulling ${base}${EVENTS_PATH} (after=${after})`);
|
|
371
|
+
while (!abort.signal.aborted) {
|
|
372
|
+
let parsed;
|
|
373
|
+
try {
|
|
374
|
+
const query = new URLSearchParams({
|
|
375
|
+
after,
|
|
376
|
+
limit: String(POLL_LIMIT),
|
|
377
|
+
waitMs: String(POLL_WAIT_MS),
|
|
378
|
+
});
|
|
379
|
+
const response = yield doFetch(`${base}${EVENTS_PATH}?${query}`, {
|
|
380
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
381
|
+
signal: AbortSignal.any([
|
|
382
|
+
abort.signal,
|
|
383
|
+
AbortSignal.timeout(POLL_WAIT_MS + 10000),
|
|
384
|
+
]),
|
|
385
|
+
});
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
void ((_a = response.body) === null || _a === void 0 ? void 0 : _a.cancel().catch(() => { }));
|
|
388
|
+
const authFailure = [401, 403, 404].includes(response.status);
|
|
389
|
+
log(`poll failed (${response.status}); backing off`);
|
|
390
|
+
yield delay(authFailure ? AUTH_FAILURE_BACKOFF_MS : backoffMs, abort.signal);
|
|
391
|
+
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
parsed = parsePollResponse(yield response.json());
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (abort.signal.aborted)
|
|
398
|
+
return;
|
|
399
|
+
log(`poll error (${error instanceof Error ? error.message : String(error)}); backing off`);
|
|
400
|
+
yield delay(backoffMs, abort.signal);
|
|
401
|
+
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
backoffMs = MIN_BACKOFF_MS;
|
|
405
|
+
if (parsed.gapDetected) {
|
|
406
|
+
log("gap detected: some deliveries were trimmed before this consumer read them");
|
|
407
|
+
}
|
|
408
|
+
for (const entry of parsed.events) {
|
|
409
|
+
if (abort.signal.aborted)
|
|
410
|
+
return;
|
|
411
|
+
const envelope = parseAgentSdkScmEventEnvelope(entry.envelope);
|
|
412
|
+
if (envelope !== undefined) {
|
|
413
|
+
for (let attempt = 1; attempt <= DISPATCH_ATTEMPTS; attempt++) {
|
|
414
|
+
if (abort.signal.aborted)
|
|
415
|
+
return;
|
|
416
|
+
try {
|
|
417
|
+
yield dispatch(envelope);
|
|
418
|
+
log(`dispatched ${envelope.repo}#${envelope.pullRequest.number} (${envelope.action}, delivery=${envelope.deliveryId})`);
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
if (abort.signal.aborted)
|
|
423
|
+
return;
|
|
424
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
425
|
+
if (attempt === DISPATCH_ATTEMPTS) {
|
|
426
|
+
// Skip, don't wedge: the offset advances so one poison
|
|
427
|
+
// delivery cannot stall the stream.
|
|
428
|
+
log(`dispatch failed for ${envelope.deliveryId} after ${attempt} attempts: ${message}`);
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
log(`dispatch attempt ${attempt} failed for ${envelope.deliveryId}: ${message}; retrying`);
|
|
432
|
+
yield delay(DISPATCH_RETRY_DELAYS_MS[Math.min(attempt - 1, DISPATCH_RETRY_DELAYS_MS.length - 1)], abort.signal);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
after = entry.offset;
|
|
437
|
+
yield offsetStore.save(after).catch(() => { });
|
|
438
|
+
}
|
|
439
|
+
if (parsed.events.length === 0 && parsed.nextOffset !== after) {
|
|
440
|
+
// "$" anchor resolution. An empty stream anchors at "" (read from
|
|
441
|
+
// the beginning), which is valid in-memory but not worth persisting
|
|
442
|
+
// (a restart re-anchors at "$" anyway).
|
|
443
|
+
after = parsed.nextOffset;
|
|
444
|
+
if (after !== "") {
|
|
445
|
+
yield offsetStore.save(after).catch(() => { });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
const done = run().catch((error) => {
|
|
452
|
+
log(`loop crashed: ${error instanceof Error ? error.message : String(error)}`);
|
|
453
|
+
});
|
|
454
|
+
return {
|
|
455
|
+
stop: () => __awaiter(this, void 0, void 0, function* () {
|
|
456
|
+
abort.abort();
|
|
457
|
+
yield done;
|
|
458
|
+
}),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/internal/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkBH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAUrB,KAAK,UAAU,EAuBhB,MAAM,aAAa,CAAC;AA2BrB,MAAM,WAAW,uBAAuB;IACtC,sDAAsD;IACtD,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3E;AAmBD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASrE;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,EAAE,CAAC,CA2B1B;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC,CA6BzD;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAAuB,GAAG;IACjC,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CACtB,GACL,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC,CAgBzD;AAWD,iFAAiF;AACjF,eAAO,MAAM,cAAc,EAAE,GAAG,CAAC,MAAM,CAIrC,CAAC;AAEH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,YAAY,CAAC,CAmEvB;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAElE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAOnE;
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/internal/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkBH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAUrB,KAAK,UAAU,EAuBhB,MAAM,aAAa,CAAC;AA2BrB,MAAM,WAAW,uBAAuB;IACtC,sDAAsD;IACtD,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3E;AAmBD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASrE;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,EAAE,CAAC,CA2B1B;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC,CA6BzD;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAAuB,GAAG;IACjC,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CACtB,GACL,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC,CAgBzD;AAWD,iFAAiF;AACjF,eAAO,MAAM,cAAc,EAAE,GAAG,CAAC,MAAM,CAIrC,CAAC;AAEH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,YAAY,CAAC,CAmEvB;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAElE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAOnE;AAovBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiB3E;AAo2BD,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,gBAAgB,CA2JnE"}
|
|
@@ -329,6 +329,20 @@ function loadAgent(ctx, agentDir, options) {
|
|
|
329
329
|
message: 'tools cannot be enforced on runtime: "cloud" yet (the Cursor SDK rejects tool restrictions on cloud agents). Remove `tools` or run the agent on the local runtime.',
|
|
330
330
|
});
|
|
331
331
|
}
|
|
332
|
+
// Per-session connection features (defineConnection auth/advertiseTools)
|
|
333
|
+
// only run on local turns: advertised tools are in-process server tools,
|
|
334
|
+
// and per-session headers cannot reach a cloud VM's MCP config. A
|
|
335
|
+
// cloud-runtime agent that declares them must fail at serve start —
|
|
336
|
+
// never run without the connection's tools or identity. (Hybrid
|
|
337
|
+
// per-send cloud sessions fail closed in the session engine.)
|
|
338
|
+
const perSessionConnection = connections.find((connection) => connection.advertiseTools === true || connection.auth !== undefined);
|
|
339
|
+
if (perSessionConnection !== undefined && runtime === "cloud") {
|
|
340
|
+
ctx.diagnostics.push({
|
|
341
|
+
severity: "error",
|
|
342
|
+
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
343
|
+
message: `MCP connection "${perSessionConnection.name}" uses advertiseTools/auth, which only run on runtime: "local". Remove them or run the agent on the local runtime.`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
332
346
|
// `execution: "agent"` tool scripts run through the shell tool. Shell is
|
|
333
347
|
// a real capability, so it is never injected — but an allowlist that
|
|
334
348
|
// strands authored scripts deserves a loud diagnostic, not silence.
|
|
@@ -936,6 +950,59 @@ function loadConnections(ctx, connectionsDir) {
|
|
|
936
950
|
});
|
|
937
951
|
continue;
|
|
938
952
|
}
|
|
953
|
+
if (connection.auth !== undefined &&
|
|
954
|
+
typeof connection.auth !== "function") {
|
|
955
|
+
ctx.diagnostics.push({
|
|
956
|
+
severity: "error",
|
|
957
|
+
path: displayPath(ctx, entryPath),
|
|
958
|
+
message: "MCP connection `auth` must be a function: (session) => { headers }.",
|
|
959
|
+
});
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
if (connection.auth !== undefined && !("url" in connection)) {
|
|
963
|
+
ctx.diagnostics.push({
|
|
964
|
+
severity: "error",
|
|
965
|
+
path: displayPath(ctx, entryPath),
|
|
966
|
+
message: "MCP connection `auth` (per-session headers) is only supported on http/sse connections (`url`).",
|
|
967
|
+
});
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (connection.advertiseTools !== undefined &&
|
|
971
|
+
typeof connection.advertiseTools !== "boolean") {
|
|
972
|
+
ctx.diagnostics.push({
|
|
973
|
+
severity: "error",
|
|
974
|
+
path: displayPath(ctx, entryPath),
|
|
975
|
+
message: "MCP connection `advertiseTools` must be a boolean.",
|
|
976
|
+
});
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (connection.advertiseTools === true &&
|
|
980
|
+
isInlineStdioConnection(connection)) {
|
|
981
|
+
ctx.diagnostics.push({
|
|
982
|
+
severity: "error",
|
|
983
|
+
path: displayPath(ctx, entryPath),
|
|
984
|
+
message: "MCP connection `advertiseTools` is not supported on inline `tools` connections — author those as agent/tools/ server tools instead.",
|
|
985
|
+
});
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
if (connection.advertiseTools === true && connection.hostOnly === true) {
|
|
989
|
+
ctx.diagnostics.push({
|
|
990
|
+
severity: "error",
|
|
991
|
+
path: displayPath(ctx, entryPath),
|
|
992
|
+
message: "MCP connection `advertiseTools` puts the connection's tools in the model's toolset, which contradicts `hostOnly` (off the model entirely). Drop one of them.",
|
|
993
|
+
});
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
if (connection.auth !== undefined &&
|
|
997
|
+
"url" in connection &&
|
|
998
|
+
connection.oauth === true) {
|
|
999
|
+
ctx.diagnostics.push({
|
|
1000
|
+
severity: "error",
|
|
1001
|
+
path: displayPath(ctx, entryPath),
|
|
1002
|
+
message: "MCP connection `oauth: true` cannot be combined with `auth` (per-session headers): the host OAuth provider owns the Authorization header. Drop one of them.",
|
|
1003
|
+
});
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
939
1006
|
if (isInlineStdioConnection(connection)) {
|
|
940
1007
|
const packed = yield loadInlineStdioConnection(ctx, name, entryPath, connection);
|
|
941
1008
|
if (packed === undefined) {
|
|
@@ -997,6 +1064,8 @@ function loadConnections(ctx, connectionsDir) {
|
|
|
997
1064
|
transport,
|
|
998
1065
|
description: connection.description,
|
|
999
1066
|
hostOnly: connection.hostOnly,
|
|
1067
|
+
auth: connection.auth,
|
|
1068
|
+
advertiseTools: connection.advertiseTools,
|
|
1000
1069
|
});
|
|
1001
1070
|
}
|
|
1002
1071
|
return connections;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-project.d.ts","sourceRoot":"","sources":["../../src/internal/init-project.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"init-project.d.ts","sourceRoot":"","sources":["../../src/internal/init-project.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,9 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
});
|
|
12
12
|
};
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
|
-
import { existsSync
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
15
|
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
16
16
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
17
|
+
import { canonicalTemplateName, isTemplateName, readCursorAgentTemplate, } from "./cursor-agent-template.js";
|
|
17
18
|
import { CLI_COMMAND_NAME, PACKAGE_NAME, packageRootDir, packageVersion, } from "./distribution.js";
|
|
18
19
|
import { applyInitReplacements, isTemplateMetaPath, loadTemplateInitSpec, posixRel, templateFileHasReplacements, } from "./init-interview.js";
|
|
19
20
|
import { makePalette, stdoutPalette, } from "./terminal-style.js";
|
|
@@ -25,9 +26,6 @@ const TEMPLATES = {
|
|
|
25
26
|
"triage-linear": { dir: "triage", overlay: "linear" },
|
|
26
27
|
"triage-jira": { dir: "triage", overlay: "jira" },
|
|
27
28
|
};
|
|
28
|
-
function canonicalTemplateName(name) {
|
|
29
|
-
return name === "triage" ? "triage-linear" : name;
|
|
30
|
-
}
|
|
31
29
|
export function availableTemplates() {
|
|
32
30
|
return Object.keys(TEMPLATES).sort();
|
|
33
31
|
}
|
|
@@ -39,13 +37,13 @@ export function resolveTemplate(name) {
|
|
|
39
37
|
};
|
|
40
38
|
}
|
|
41
39
|
const resolved = canonicalTemplateName(name);
|
|
42
|
-
|
|
43
|
-
if (entry === undefined) {
|
|
40
|
+
if (!isTemplateName(resolved)) {
|
|
44
41
|
return {
|
|
45
42
|
ok: false,
|
|
46
43
|
message: `unknown template "${name}" (available: ${availableTemplates().join(", ")})`,
|
|
47
44
|
};
|
|
48
45
|
}
|
|
46
|
+
const entry = TEMPLATES[resolved];
|
|
49
47
|
const dir = join(packageRootDir(), "templates", entry.dir);
|
|
50
48
|
return {
|
|
51
49
|
ok: true,
|
|
@@ -67,17 +65,7 @@ export function checkInitTarget(rootDir, template) {
|
|
|
67
65
|
if (!existsSync(pkgPath)) {
|
|
68
66
|
return { ok: true };
|
|
69
67
|
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
73
|
-
marker =
|
|
74
|
-
typeof pkg.cursorAgentTemplate === "string"
|
|
75
|
-
? pkg.cursorAgentTemplate
|
|
76
|
-
: undefined;
|
|
77
|
-
}
|
|
78
|
-
catch (_a) {
|
|
79
|
-
marker = undefined;
|
|
80
|
-
}
|
|
68
|
+
const marker = readCursorAgentTemplate(rootDir);
|
|
81
69
|
if (marker === template) {
|
|
82
70
|
return { ok: true };
|
|
83
71
|
}
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
* {@link HostMcpRegistry} on {@link ChannelHandlerArgs.host}.mcp
|
|
10
10
|
*/
|
|
11
11
|
import type { DiscoveredConnection, HostMcpCallResult, HostMcpRegistry, HostMcpToolInfo } from "../types.js";
|
|
12
|
+
/**
|
|
13
|
+
* A non-caching view of one connection: every operation opens a fresh
|
|
14
|
+
* client and closes it afterwards. See {@link McpHost.oneOff}.
|
|
15
|
+
*/
|
|
16
|
+
export interface HostMcpOneOff {
|
|
17
|
+
listTools(): Promise<HostMcpToolInfo[]>;
|
|
18
|
+
callTool(toolName: string, args?: Record<string, unknown>): Promise<HostMcpCallResult>;
|
|
19
|
+
}
|
|
12
20
|
export declare class McpHost implements HostMcpRegistry {
|
|
13
21
|
private readonly byName;
|
|
14
22
|
private readonly clients;
|
|
@@ -20,6 +28,16 @@ export declare class McpHost implements HostMcpRegistry {
|
|
|
20
28
|
addConnection(connection: DiscoveredConnection): void;
|
|
21
29
|
names(): string[];
|
|
22
30
|
listTools(connectionName: string): Promise<HostMcpToolInfo[]>;
|
|
31
|
+
/**
|
|
32
|
+
* A one-off view of a connection that is not (or not exactly) in the
|
|
33
|
+
* registry — e.g. a registered connection with per-session auth headers
|
|
34
|
+
* substituted in. Every operation opens a fresh client and closes it
|
|
35
|
+
* afterwards: the registry's cached per-name client would leak one
|
|
36
|
+
* session's identity into another, and stateless streamable-HTTP
|
|
37
|
+
* servers answer a fresh initialize cheaply. Results ride the same
|
|
38
|
+
* mapping as registry calls.
|
|
39
|
+
*/
|
|
40
|
+
oneOff(connection: DiscoveredConnection): HostMcpOneOff;
|
|
23
41
|
callTool(connectionName: string, toolName: string, args?: Record<string, unknown>): Promise<HostMcpCallResult>;
|
|
24
42
|
close(): Promise<void>;
|
|
25
43
|
/**
|