@cursor/july 0.1.2 → 0.1.3
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/AGENTS.md +16 -5
- package/README.md +8 -9
- package/dist/bin/agent-serve.d.ts +3 -1
- package/dist/bin/agent-serve.d.ts.map +1 -1
- package/dist/bin/agent-serve.js +389 -152
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.Oje4vhlk.js → app.BR0RbIdx.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DtIOQzGj.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.qjsVTneI.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.2Kg8jIp_.js} +2 -2
- package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.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/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.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/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 +22 -10
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +14 -9
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/internal/chat-client.d.ts +29 -3
- package/dist/internal/chat-client.d.ts.map +1 -1
- package/dist/internal/chat-client.js +180 -27
- package/dist/internal/cli-ax.d.ts +75 -2
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +600 -52
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +8 -36
- package/dist/internal/cli-deploy.d.ts +59 -3
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +316 -38
- package/dist/internal/cursor/credentials.d.ts +9 -0
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +12 -0
- package/dist/internal/deploy-client.d.ts +9 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +23 -3
- package/dist/internal/deploy-source.d.ts +35 -0
- package/dist/internal/deploy-source.d.ts.map +1 -0
- package/dist/internal/deploy-source.js +118 -0
- package/dist/internal/discovery.d.ts +11 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +43 -14
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +2 -1
- package/dist/internal/eval-runner.d.ts +2 -0
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +2 -0
- package/dist/internal/event-mapper.d.ts +54 -1
- package/dist/internal/event-mapper.d.ts.map +1 -1
- package/dist/internal/event-mapper.js +151 -41
- package/dist/internal/init-project.d.ts +88 -1
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +221 -31
- package/dist/internal/install-cursor-skills.d.ts +64 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -0
- package/dist/internal/install-cursor-skills.js +274 -0
- package/dist/internal/logs-client.d.ts +60 -0
- package/dist/internal/logs-client.d.ts.map +1 -0
- package/dist/internal/logs-client.js +311 -0
- package/dist/internal/open-browser.d.ts +5 -0
- package/dist/internal/open-browser.d.ts.map +1 -0
- package/dist/internal/open-browser.js +34 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -1
- package/dist/internal/playground/toolchain.js +16 -11
- package/dist/internal/playground-proxy.d.ts +69 -0
- package/dist/internal/playground-proxy.d.ts.map +1 -0
- package/dist/internal/playground-proxy.js +468 -0
- package/dist/internal/prompt-context.d.ts +35 -0
- package/dist/internal/prompt-context.d.ts.map +1 -0
- package/dist/internal/prompt-context.js +71 -0
- package/dist/internal/request-headers.d.ts +11 -0
- package/dist/internal/request-headers.d.ts.map +1 -0
- package/dist/internal/request-headers.js +14 -0
- package/dist/internal/resolve-prod-target.d.ts +42 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -0
- package/dist/internal/resolve-prod-target.js +111 -0
- package/dist/internal/run-client.d.ts +2 -2
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +38 -23
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +10 -4
- package/dist/internal/session-engine.d.ts +1 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +21 -6
- package/dist/internal/sessions-client.d.ts +21 -0
- package/dist/internal/sessions-client.d.ts.map +1 -0
- package/dist/internal/sessions-client.js +168 -0
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +31 -3
- package/dist/internal/terminal-style.d.ts +22 -0
- package/dist/internal/terminal-style.d.ts.map +1 -0
- package/dist/internal/terminal-style.js +49 -0
- package/dist/internal/trajectory.d.ts +10 -5
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +82 -10
- package/dist/internal/workspace.d.ts +11 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +38 -4
- package/dist/playground/assets/index-D-vo2lV_.css +1 -0
- package/dist/playground/assets/index-x60b9q2j.js +312 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +26 -1
- package/dist/types.d.ts.map +1 -1
- package/docs/quickstart.md +22 -7
- package/docs/reference/cli.md +51 -3
- package/package.json +3 -1
- package/skills/ab/SKILL.md +8 -8
- package/skills/create-agent/SKILL.md +28 -22
- package/skills/debug/SKILL.md +11 -11
- package/skills/evals/SKILL.md +16 -16
- package/skills/framework-map/SKILL.md +6 -6
- package/skills/github/SKILL.md +13 -13
- package/skills/hillclimb/SKILL.md +10 -10
- package/skills/setup-slack/SKILL.md +13 -13
- package/src/bin/agent-serve.ts +422 -188
- package/src/internal/chat-client.ts +252 -37
- package/src/internal/cli-ax.ts +722 -72
- package/src/internal/cli-cursor.ts +14 -42
- package/src/internal/cli-deploy.ts +428 -49
- package/src/internal/cursor/credentials.ts +14 -0
- package/src/internal/deploy-client.ts +45 -4
- package/src/internal/deploy-source.ts +133 -0
- package/src/internal/discovery.ts +53 -16
- package/src/internal/eval-run-store.ts +2 -1
- package/src/internal/eval-runner.ts +5 -0
- package/src/internal/event-mapper.ts +222 -42
- package/src/internal/init-project.ts +333 -51
- package/src/internal/install-cursor-skills.ts +327 -0
- package/src/internal/logs-client.ts +442 -0
- package/src/internal/open-browser.ts +41 -0
- package/src/internal/playground/toolchain.ts +15 -13
- package/src/internal/playground-proxy.ts +576 -0
- package/src/internal/prompt-context.ts +95 -0
- package/src/internal/request-headers.ts +23 -0
- package/src/internal/resolve-prod-target.ts +150 -0
- package/src/internal/run-client.ts +39 -36
- package/src/internal/server.ts +12 -3
- package/src/internal/session-engine.ts +22 -3
- package/src/internal/sessions-client.ts +182 -0
- package/src/internal/stream-progress.ts +36 -2
- package/src/internal/terminal-style.ts +74 -0
- package/src/internal/trajectory.ts +91 -13
- package/src/internal/workspace.ts +42 -4
- package/src/types.ts +42 -6
- package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
- package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
- package/dist/playground/assets/index-CpDYCj8W.js +0 -79
- /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
|
@@ -13,7 +13,7 @@ import { basename, resolve } from "node:path";
|
|
|
13
13
|
import type { AgentHostingOptions } from "../types.js";
|
|
14
14
|
import { CursorBackendClient } from "./cursor/backend-client.js";
|
|
15
15
|
import {
|
|
16
|
-
|
|
16
|
+
cursorExternalApiUrl,
|
|
17
17
|
resolveApiKey,
|
|
18
18
|
SIGN_IN_HINT,
|
|
19
19
|
} from "./cursor/credentials.js";
|
|
@@ -31,29 +31,63 @@ import {
|
|
|
31
31
|
TERMINAL_DEPLOY_STATUSES,
|
|
32
32
|
validateDeploymentSlug,
|
|
33
33
|
} from "./deploy-client.js";
|
|
34
|
-
import {
|
|
34
|
+
import { type GitRunner, inferDeployGitSource } from "./deploy-source.js";
|
|
35
|
+
import {
|
|
36
|
+
type ChildAgentDir,
|
|
37
|
+
isAgentProjectDir,
|
|
38
|
+
listChildAgentDirs,
|
|
39
|
+
loadAgentProject,
|
|
40
|
+
} from "./discovery.js";
|
|
35
41
|
import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
|
|
36
42
|
import {
|
|
37
43
|
isValidCursorEventRepo,
|
|
38
44
|
validateSettableSecretName,
|
|
39
45
|
} from "./hosting.js";
|
|
46
|
+
import { stderrPalette } from "./terminal-style.js";
|
|
40
47
|
|
|
41
48
|
const DEFAULT_POLL_INTERVAL_MS = 3_000;
|
|
42
49
|
const DEFAULT_DEPLOY_TIMEOUT_MS = 10 * 60 * 1000;
|
|
43
50
|
|
|
51
|
+
/** One agent directory selected for deploy (single or multi-agent parent). */
|
|
52
|
+
export type DeployTarget = ChildAgentDir;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Interactive / test seam: pick which agents under a multi-agent parent to
|
|
56
|
+
* deploy. Return `null` to cancel.
|
|
57
|
+
*/
|
|
58
|
+
export type ChooseDeployTargets = (
|
|
59
|
+
agents: readonly DeployTarget[]
|
|
60
|
+
) => Promise<DeployTarget[] | null>;
|
|
61
|
+
|
|
44
62
|
export interface DeployCliOptions {
|
|
45
63
|
dir?: string;
|
|
46
64
|
slug?: string;
|
|
65
|
+
/**
|
|
66
|
+
* deploy `--all`: when `--dir` is a multi-agent parent, deploy every
|
|
67
|
+
* child agent (skips the interactive picker).
|
|
68
|
+
*/
|
|
69
|
+
all?: boolean;
|
|
47
70
|
/** `--team <id>`; resolved from the account when omitted. */
|
|
48
71
|
team?: string;
|
|
49
72
|
json?: boolean;
|
|
50
73
|
noWait?: boolean;
|
|
51
74
|
apiKey?: string;
|
|
52
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* deploy `--repo <url>`: https git repo the control plane builds from.
|
|
77
|
+
* Optional — inferred from `git remote origin` when omitted inside an
|
|
78
|
+
* agent project checkout.
|
|
79
|
+
*/
|
|
53
80
|
repo?: string;
|
|
54
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* deploy `--ref <ref>`: git ref to build. Optional — inferred from the
|
|
83
|
+
* current branch (or HEAD SHA if detached) when omitted; otherwise the
|
|
84
|
+
* server uses the repo's default branch.
|
|
85
|
+
*/
|
|
55
86
|
ref?: string;
|
|
56
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* deploy `--path <agentPath>`: agent directory relative to the repo root.
|
|
89
|
+
* Optional — inferred from `--dir` vs the git toplevel when omitted.
|
|
90
|
+
*/
|
|
57
91
|
path?: string;
|
|
58
92
|
/** deploy `--cursor-events-repo <owner/name>` (repeatable). */
|
|
59
93
|
cursorEventsRepos?: string[];
|
|
@@ -61,6 +95,10 @@ export interface DeployCliOptions {
|
|
|
61
95
|
allowDomains?: string[];
|
|
62
96
|
/** Test seams. */
|
|
63
97
|
fetchImpl?: typeof fetch;
|
|
98
|
+
/** Inject git probing for deploy source inference (tests). */
|
|
99
|
+
git?: GitRunner;
|
|
100
|
+
/** Inject the multi-agent picker (tests / non-interactive hosts). */
|
|
101
|
+
chooseTargets?: ChooseDeployTargets;
|
|
64
102
|
pollIntervalMs?: number;
|
|
65
103
|
timeoutMs?: number;
|
|
66
104
|
out?: (text: string) => void;
|
|
@@ -86,15 +124,27 @@ interface DeployCliContext {
|
|
|
86
124
|
err: (text: string) => void;
|
|
87
125
|
}
|
|
88
126
|
|
|
127
|
+
export interface ResolveDeployAuthOptions {
|
|
128
|
+
team?: string;
|
|
129
|
+
apiKey?: string;
|
|
130
|
+
fetchImpl?: typeof fetch;
|
|
131
|
+
/** Quiet team-resolution chatter (default: write to stderr). */
|
|
132
|
+
err?: (text: string) => void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface ResolvedDeployAuth {
|
|
136
|
+
client: AgentServeDeployClient;
|
|
137
|
+
teamId: number;
|
|
138
|
+
}
|
|
139
|
+
|
|
89
140
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
141
|
+
* Credentials → deploy client → team id. Shared by deploy commands and
|
|
142
|
+
* `--prod` AX routing. Returns an exit code (error already rendered) on
|
|
143
|
+
* failure.
|
|
92
144
|
*/
|
|
93
|
-
async function
|
|
94
|
-
options:
|
|
95
|
-
): Promise<
|
|
96
|
-
const out =
|
|
97
|
-
options.out ?? ((text: string) => void process.stdout.write(text));
|
|
145
|
+
export async function resolveDeployAuth(
|
|
146
|
+
options: ResolveDeployAuthOptions
|
|
147
|
+
): Promise<ResolvedDeployAuth | number> {
|
|
98
148
|
const err =
|
|
99
149
|
options.err ?? ((text: string) => void process.stderr.write(text));
|
|
100
150
|
|
|
@@ -105,12 +155,13 @@ async function prepare(
|
|
|
105
155
|
err(`Not signed in to Cursor. ${SIGN_IN_HINT}\n`);
|
|
106
156
|
return 1;
|
|
107
157
|
}
|
|
158
|
+
// The deployment management routes live on the external API host
|
|
159
|
+
// (api.cursor.com), not the default backend (api2.cursor.sh) — see
|
|
160
|
+
// cursorExternalApiUrl. GetMe below stays on the resolved backend.
|
|
108
161
|
const client = new AgentServeDeployClient({
|
|
109
|
-
backendUrl:
|
|
162
|
+
backendUrl: cursorExternalApiUrl(),
|
|
110
163
|
apiKey: resolved.apiKey,
|
|
111
|
-
|
|
112
|
-
? {}
|
|
113
|
-
: { fetchImpl: options.fetchImpl }),
|
|
164
|
+
fetchImpl: options.fetchImpl,
|
|
114
165
|
});
|
|
115
166
|
|
|
116
167
|
let teamId: number;
|
|
@@ -124,9 +175,7 @@ async function prepare(
|
|
|
124
175
|
const account = new CursorBackendClient({
|
|
125
176
|
backendUrl: resolved.backendUrl,
|
|
126
177
|
apiKey: resolved.apiKey,
|
|
127
|
-
|
|
128
|
-
? {}
|
|
129
|
-
: { fetchImpl: options.fetchImpl }),
|
|
178
|
+
fetchImpl: options.fetchImpl,
|
|
130
179
|
});
|
|
131
180
|
let identity: Awaited<ReturnType<CursorBackendClient["getMe"]>>;
|
|
132
181
|
try {
|
|
@@ -144,16 +193,44 @@ async function prepare(
|
|
|
144
193
|
return 1;
|
|
145
194
|
}
|
|
146
195
|
teamId = identity.teamId;
|
|
196
|
+
const dim = stderrPalette().dim;
|
|
147
197
|
err(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
198
|
+
`${dim(
|
|
199
|
+
`Using team ${teamId}${
|
|
200
|
+
identity.teamName === undefined ? "" : ` (${identity.teamName})`
|
|
201
|
+
} — the account's team; pass --team to override.`
|
|
202
|
+
)}\n`
|
|
151
203
|
);
|
|
152
204
|
}
|
|
153
205
|
|
|
206
|
+
return { client, teamId };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Shared preamble: credentials → client → team id. Returns an exit code
|
|
211
|
+
* (with the error already rendered) when resolution fails.
|
|
212
|
+
*/
|
|
213
|
+
async function prepare(
|
|
214
|
+
options: DeployCliOptions
|
|
215
|
+
): Promise<DeployCliContext | number> {
|
|
216
|
+
const out =
|
|
217
|
+
options.out ?? ((text: string) => void process.stdout.write(text));
|
|
218
|
+
const err =
|
|
219
|
+
options.err ?? ((text: string) => void process.stderr.write(text));
|
|
220
|
+
|
|
221
|
+
const auth = await resolveDeployAuth({
|
|
222
|
+
team: options.team,
|
|
223
|
+
apiKey: options.apiKey,
|
|
224
|
+
fetchImpl: options.fetchImpl,
|
|
225
|
+
err,
|
|
226
|
+
});
|
|
227
|
+
if (typeof auth === "number") {
|
|
228
|
+
return auth;
|
|
229
|
+
}
|
|
230
|
+
|
|
154
231
|
return {
|
|
155
|
-
client,
|
|
156
|
-
teamId,
|
|
232
|
+
client: auth.client,
|
|
233
|
+
teamId: auth.teamId,
|
|
157
234
|
json: options.json === true,
|
|
158
235
|
noWait: options.noWait === true,
|
|
159
236
|
pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
|
|
@@ -172,30 +249,308 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
172
249
|
if (typeof ctx === "number") {
|
|
173
250
|
return ctx;
|
|
174
251
|
}
|
|
252
|
+
|
|
253
|
+
const targets = await resolveDeployTargets(options, ctx);
|
|
254
|
+
if (typeof targets === "number") {
|
|
255
|
+
return targets;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// --path is per-agent (relative to the repo root). When deploying several
|
|
259
|
+
// agents, ignore a shared --path so each target can infer its own path.
|
|
260
|
+
let deployOptions = options;
|
|
261
|
+
if (targets.length > 1 && options.path !== undefined) {
|
|
262
|
+
if (!ctx.json) {
|
|
263
|
+
ctx.err(
|
|
264
|
+
`warning: ignoring --path ${JSON.stringify(options.path)} for a multi-agent deploy; each agent infers its own path from git.\n`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
deployOptions = { ...options, path: undefined };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (targets.length === 1) {
|
|
271
|
+
return deployOne(ctx, deployOptions, targets[0]!);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Multi-agent batch: deploy sequentially; --json emits an array.
|
|
275
|
+
const jsonResults: unknown[] = [];
|
|
276
|
+
let failures = 0;
|
|
277
|
+
for (const [index, target] of targets.entries()) {
|
|
278
|
+
if (!ctx.json) {
|
|
279
|
+
ctx.err(
|
|
280
|
+
`\n=== [${index + 1}/${targets.length}] Deploying ${target.slug} ===\n`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const before = jsonResults.length;
|
|
284
|
+
const code = await deployOne(ctx, deployOptions, target, {
|
|
285
|
+
jsonCollect: ctx.json ? jsonResults : undefined,
|
|
286
|
+
});
|
|
287
|
+
if (code !== 0) {
|
|
288
|
+
failures += 1;
|
|
289
|
+
if (ctx.json && jsonResults.length === before) {
|
|
290
|
+
jsonResults.push({ slug: target.slug, error: "deploy failed" });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (ctx.json) {
|
|
295
|
+
ctx.out(`${JSON.stringify(jsonResults, null, 2)}\n`);
|
|
296
|
+
} else if (failures > 0) {
|
|
297
|
+
ctx.err(`\n${failures} of ${targets.length} deploy(s) failed.\n`);
|
|
298
|
+
} else {
|
|
299
|
+
ctx.err(`\nDeployed ${targets.length} agents.\n`);
|
|
300
|
+
}
|
|
301
|
+
return failures === 0 ? 0 : 1;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Resolve which agent directory(ies) to deploy from `--dir` / `--slug` /
|
|
306
|
+
* `--all`, with an interactive picker when a multi-agent parent is on a TTY.
|
|
307
|
+
*/
|
|
308
|
+
async function resolveDeployTargets(
|
|
309
|
+
options: DeployCliOptions,
|
|
310
|
+
ctx: DeployCliContext
|
|
311
|
+
): Promise<DeployTarget[] | number> {
|
|
175
312
|
const dir = resolve(options.dir ?? ".");
|
|
176
|
-
|
|
313
|
+
|
|
177
314
|
if (options.slug !== undefined) {
|
|
178
315
|
const slugError = validateDeploymentSlug(options.slug);
|
|
179
316
|
if (slugError !== undefined) {
|
|
180
317
|
ctx.err(`${slugError}\n`);
|
|
181
318
|
return 1;
|
|
182
319
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
320
|
+
}
|
|
321
|
+
if (options.all === true && options.slug !== undefined) {
|
|
322
|
+
ctx.err("Pass either --slug or --all, not both.\n");
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (await isAgentProjectDir(dir)) {
|
|
327
|
+
if (options.all === true) {
|
|
187
328
|
ctx.err(
|
|
188
|
-
|
|
329
|
+
`--all requires a multi-agent parent directory (several child agents under --dir); ${dir} is a single agent project.\n`
|
|
189
330
|
);
|
|
190
331
|
return 1;
|
|
191
332
|
}
|
|
192
|
-
slug
|
|
193
|
-
|
|
333
|
+
let slug: string;
|
|
334
|
+
if (options.slug !== undefined) {
|
|
335
|
+
slug = options.slug;
|
|
336
|
+
} else {
|
|
337
|
+
const derived = slugFromDirectoryName(basename(dir));
|
|
338
|
+
if (derived === undefined) {
|
|
339
|
+
ctx.err(
|
|
340
|
+
`Could not derive a deployment slug from the directory name ${JSON.stringify(basename(dir))}; pass --slug.\n`
|
|
341
|
+
);
|
|
342
|
+
return 1;
|
|
343
|
+
}
|
|
344
|
+
slug = derived;
|
|
345
|
+
if (!ctx.json) {
|
|
346
|
+
ctx.err(`Deploying as "${slug}" (from the directory name).\n`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return [{ slug, dir }];
|
|
194
350
|
}
|
|
195
351
|
|
|
196
|
-
|
|
352
|
+
let children: ChildAgentDir[];
|
|
353
|
+
try {
|
|
354
|
+
children = await listChildAgentDirs(dir);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
ctx.err(`${describeError(error)}\n`);
|
|
357
|
+
return 1;
|
|
358
|
+
}
|
|
359
|
+
// Deploy slugs are lowercase-only; normalize directory names the same way
|
|
360
|
+
// single-agent deploy does so MixedCase child dirs still deploy.
|
|
361
|
+
children = children.flatMap((child) => {
|
|
362
|
+
const slug = slugFromDirectoryName(basename(child.dir));
|
|
363
|
+
if (slug === undefined) {
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
return [{ slug, dir: child.dir }];
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
if (children.length === 0) {
|
|
370
|
+
if (options.all === true) {
|
|
371
|
+
ctx.err(
|
|
372
|
+
`--all requires child agent projects under --dir; none found in ${dir}.\n`
|
|
373
|
+
);
|
|
374
|
+
return 1;
|
|
375
|
+
}
|
|
376
|
+
// Static / bare-slug deploy against a non-agent directory.
|
|
377
|
+
let slug: string;
|
|
378
|
+
if (options.slug !== undefined) {
|
|
379
|
+
slug = options.slug;
|
|
380
|
+
} else {
|
|
381
|
+
const derived = slugFromDirectoryName(basename(dir));
|
|
382
|
+
if (derived === undefined) {
|
|
383
|
+
ctx.err(
|
|
384
|
+
`No agent projects under ${dir}, and could not derive a slug from ${JSON.stringify(basename(dir))}; pass --slug or --dir <agent>.\n`
|
|
385
|
+
);
|
|
386
|
+
return 1;
|
|
387
|
+
}
|
|
388
|
+
slug = derived;
|
|
389
|
+
if (!ctx.json) {
|
|
390
|
+
ctx.err(
|
|
391
|
+
`Deploying as "${slug}" (static; no agent project at ${dir}).\n`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return [{ slug, dir }];
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (children.length === 1) {
|
|
399
|
+
const only = children[0]!;
|
|
400
|
+
if (options.all === true) {
|
|
401
|
+
// One child is fine for --all (degenerate multi-agent parent).
|
|
402
|
+
} else if (
|
|
403
|
+
options.slug !== undefined &&
|
|
404
|
+
options.slug.toLowerCase() !== only.slug
|
|
405
|
+
) {
|
|
406
|
+
ctx.err(
|
|
407
|
+
`No agent "${options.slug}" under ${dir}; found "${only.slug}".\n`
|
|
408
|
+
);
|
|
409
|
+
return 1;
|
|
410
|
+
}
|
|
411
|
+
if (!ctx.json) {
|
|
412
|
+
ctx.err(
|
|
413
|
+
`Deploying "${only.slug}" (only agent under ${basename(dir)}).\n`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return [only];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Multiple children.
|
|
420
|
+
if (options.all === true) {
|
|
421
|
+
if (!ctx.json) {
|
|
422
|
+
ctx.err(
|
|
423
|
+
`Deploying all ${children.length} agents under ${basename(dir)}: ${children.map((c) => c.slug).join(", ")}.\n`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return [...children];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (options.slug !== undefined) {
|
|
430
|
+
const wanted = options.slug.toLowerCase();
|
|
431
|
+
const match = children.find((child) => child.slug === wanted);
|
|
432
|
+
if (match === undefined) {
|
|
433
|
+
ctx.err(
|
|
434
|
+
`No agent "${options.slug}" under ${dir}.\n` +
|
|
435
|
+
formatAgentList(children) +
|
|
436
|
+
`Pass --slug <name> or --all.\n`
|
|
437
|
+
);
|
|
438
|
+
return 1;
|
|
439
|
+
}
|
|
440
|
+
return [match];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const chooser =
|
|
444
|
+
options.chooseTargets ??
|
|
445
|
+
((agents) =>
|
|
446
|
+
promptChooseDeployTargets(agents, {
|
|
447
|
+
err: ctx.err,
|
|
448
|
+
stdin: options.stdin ?? process.stdin,
|
|
449
|
+
interactive:
|
|
450
|
+
ctx.json !== true &&
|
|
451
|
+
(options.stdin ?? process.stdin).isTTY === true &&
|
|
452
|
+
process.stderr.isTTY === true,
|
|
453
|
+
}));
|
|
454
|
+
const chosen = await chooser(children);
|
|
455
|
+
if (chosen === null) {
|
|
456
|
+
ctx.err("Deploy cancelled.\n");
|
|
457
|
+
return 1;
|
|
458
|
+
}
|
|
459
|
+
if (chosen.length === 0) {
|
|
460
|
+
ctx.err(
|
|
461
|
+
`Multiple agents under ${dir}:\n` +
|
|
462
|
+
formatAgentList(children) +
|
|
463
|
+
`Pass --slug <name> or --all (or re-run on a TTY to pick interactively).\n`
|
|
464
|
+
);
|
|
465
|
+
return 1;
|
|
466
|
+
}
|
|
467
|
+
return chosen;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function formatAgentList(agents: readonly DeployTarget[]): string {
|
|
471
|
+
return agents.map((agent, i) => ` ${i + 1}) ${agent.slug}\n`).join("");
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* TTY picker for multi-agent parents. Non-interactive callers get `[]`
|
|
476
|
+
* so {@link resolveDeployTargets} can print the --slug/--all hint.
|
|
477
|
+
*/
|
|
478
|
+
export async function promptChooseDeployTargets(
|
|
479
|
+
agents: readonly DeployTarget[],
|
|
480
|
+
options: {
|
|
481
|
+
err: (text: string) => void;
|
|
482
|
+
stdin: SecretInputStream;
|
|
483
|
+
interactive: boolean;
|
|
484
|
+
}
|
|
485
|
+
): Promise<DeployTarget[] | null> {
|
|
486
|
+
options.err(
|
|
487
|
+
`Multiple agents (${agents.length}):\n` +
|
|
488
|
+
formatAgentList(agents) +
|
|
489
|
+
` a) all\n`
|
|
490
|
+
);
|
|
491
|
+
if (!options.interactive) {
|
|
492
|
+
return [];
|
|
493
|
+
}
|
|
494
|
+
options.err('Deploy which? Enter a number, slug, "all", or "q" to cancel: ');
|
|
495
|
+
const readLine = createLineReader(options.stdin);
|
|
496
|
+
// createLineReader pauses until data; ensure stdin is flowing on a TTY.
|
|
497
|
+
options.stdin.resume?.();
|
|
498
|
+
const answer = (await readLine())?.trim().toLowerCase();
|
|
499
|
+
if (answer === undefined || answer === "q" || answer === "quit") {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
if (answer === "a" || answer === "all" || answer === "*") {
|
|
503
|
+
return [...agents];
|
|
504
|
+
}
|
|
505
|
+
const asIndex = Number.parseInt(answer, 10);
|
|
506
|
+
if (
|
|
507
|
+
!Number.isNaN(asIndex) &&
|
|
508
|
+
String(asIndex) === answer &&
|
|
509
|
+
asIndex >= 1 &&
|
|
510
|
+
asIndex <= agents.length
|
|
511
|
+
) {
|
|
512
|
+
return [agents[asIndex - 1]!];
|
|
513
|
+
}
|
|
514
|
+
const bySlug = agents.find((agent) => agent.slug.toLowerCase() === answer);
|
|
515
|
+
if (bySlug !== undefined) {
|
|
516
|
+
return [bySlug];
|
|
517
|
+
}
|
|
518
|
+
options.err(
|
|
519
|
+
`Unrecognized choice ${JSON.stringify(answer)} — expected 1–${agents.length}, a slug, "all", or "q". Aborting.\n`
|
|
520
|
+
);
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async function deployOne(
|
|
525
|
+
ctx: DeployCliContext,
|
|
526
|
+
options: DeployCliOptions,
|
|
527
|
+
target: DeployTarget,
|
|
528
|
+
extras?: { jsonCollect?: unknown[] }
|
|
529
|
+
): Promise<number> {
|
|
530
|
+
const { dir, slug } = target;
|
|
531
|
+
|
|
532
|
+
// Fill --repo/--ref/--path from the local git checkout when omitted so
|
|
533
|
+
// `agentkit deploy` inside an agent directory is enough. Only probe git
|
|
534
|
+
// for real agent projects (avoids ambient monorepo remotes when someone
|
|
535
|
+
// deploys a bare --slug). Explicit flags always win.
|
|
536
|
+
const isAgent = await isAgentProjectDir(dir);
|
|
537
|
+
const needsGitInference =
|
|
538
|
+
isAgent &&
|
|
539
|
+
(options.repo === undefined ||
|
|
540
|
+
options.ref === undefined ||
|
|
541
|
+
options.path === undefined);
|
|
542
|
+
const inferred = needsGitInference
|
|
543
|
+
? await inferDeployGitSource(dir, {
|
|
544
|
+
...(options.git === undefined ? {} : { git: options.git }),
|
|
545
|
+
})
|
|
546
|
+
: {};
|
|
547
|
+
const gitRepoUrl = options.repo ?? inferred.gitRepoUrl;
|
|
548
|
+
const gitRef = options.ref ?? inferred.gitRef;
|
|
549
|
+
const agentPath = options.path ?? inferred.agentPath;
|
|
550
|
+
|
|
551
|
+
if (gitRepoUrl !== undefined && !gitRepoUrl.startsWith("https://")) {
|
|
197
552
|
ctx.err(
|
|
198
|
-
`Invalid --repo: ${
|
|
553
|
+
`Invalid --repo: ${gitRepoUrl} (expected an https:// git repository URL)\n`
|
|
199
554
|
);
|
|
200
555
|
return 1;
|
|
201
556
|
}
|
|
@@ -206,11 +561,31 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
206
561
|
}
|
|
207
562
|
}
|
|
208
563
|
|
|
564
|
+
if (!ctx.json && gitRepoUrl !== undefined) {
|
|
565
|
+
const inferredBits: string[] = [];
|
|
566
|
+
if (options.repo === undefined && inferred.gitRepoUrl !== undefined) {
|
|
567
|
+
inferredBits.push("repo");
|
|
568
|
+
}
|
|
569
|
+
if (options.ref === undefined && inferred.gitRef !== undefined) {
|
|
570
|
+
inferredBits.push("ref");
|
|
571
|
+
}
|
|
572
|
+
if (options.path === undefined && inferred.agentPath !== undefined) {
|
|
573
|
+
inferredBits.push("path");
|
|
574
|
+
}
|
|
575
|
+
ctx.err(
|
|
576
|
+
` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
|
|
577
|
+
(agentPath === undefined ? "" : ` path: ${agentPath}\n`) +
|
|
578
|
+
(inferredBits.length === 0
|
|
579
|
+
? ""
|
|
580
|
+
: ` (inferred ${inferredBits.join(", ")} from git; pass --repo/--ref/--path to override)\n`)
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
209
584
|
// The package manifest (`defineAgent({ hosting })`) is the source of
|
|
210
585
|
// truth for egress domains and expected secret names; flags override or
|
|
211
586
|
// augment it. Directories without an agent project (static placeholder
|
|
212
587
|
// deploys) simply have no manifest.
|
|
213
|
-
const hosting = await readHostingManifest(dir);
|
|
588
|
+
const hosting = isAgent ? await readHostingManifest(dir) : undefined;
|
|
214
589
|
const egressAllowedDomains = [
|
|
215
590
|
...new Set([
|
|
216
591
|
...(hosting?.egressDomains ?? []),
|
|
@@ -222,9 +597,9 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
222
597
|
// serves the static placeholder). See DeploySource for the artifact seam.
|
|
223
598
|
const source: DeploySource = {
|
|
224
599
|
kind: "git-ref",
|
|
225
|
-
...(
|
|
226
|
-
...(
|
|
227
|
-
...(
|
|
600
|
+
...(gitRepoUrl === undefined ? {} : { gitRepoUrl }),
|
|
601
|
+
...(gitRef === undefined ? {} : { gitRef }),
|
|
602
|
+
...(agentPath === undefined ? {} : { agentPath }),
|
|
228
603
|
...(options.cursorEventsRepos === undefined ||
|
|
229
604
|
options.cursorEventsRepos.length === 0
|
|
230
605
|
? {}
|
|
@@ -245,7 +620,9 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
245
620
|
}
|
|
246
621
|
|
|
247
622
|
if (ctx.noWait) {
|
|
248
|
-
if (
|
|
623
|
+
if (extras?.jsonCollect !== undefined) {
|
|
624
|
+
extras.jsonCollect.push(accepted);
|
|
625
|
+
} else if (ctx.json) {
|
|
249
626
|
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
250
627
|
} else {
|
|
251
628
|
ctx.out(
|
|
@@ -266,16 +643,18 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
266
643
|
|
|
267
644
|
warnMissingSecrets(ctx, slug, hosting?.secretNames, detail.secretNames);
|
|
268
645
|
|
|
646
|
+
const payload =
|
|
647
|
+
accepted.aliasToken === undefined
|
|
648
|
+
? detail
|
|
649
|
+
: { ...detail, aliasToken: accepted.aliasToken };
|
|
650
|
+
|
|
651
|
+
if (extras?.jsonCollect !== undefined) {
|
|
652
|
+
extras.jsonCollect.push(payload);
|
|
653
|
+
return detail.status === "running" ? 0 : 1;
|
|
654
|
+
}
|
|
655
|
+
|
|
269
656
|
if (ctx.json) {
|
|
270
|
-
ctx.out(
|
|
271
|
-
`${JSON.stringify(
|
|
272
|
-
accepted.aliasToken === undefined
|
|
273
|
-
? detail
|
|
274
|
-
: { ...detail, aliasToken: accepted.aliasToken },
|
|
275
|
-
null,
|
|
276
|
-
2
|
|
277
|
-
)}\n`
|
|
278
|
-
);
|
|
657
|
+
ctx.out(`${JSON.stringify(payload, null, 2)}\n`);
|
|
279
658
|
return detail.status === "running" ? 0 : 1;
|
|
280
659
|
}
|
|
281
660
|
|
|
@@ -295,7 +674,7 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
295
674
|
` generation: ${detail.generation}\n` +
|
|
296
675
|
` kind: ${detail.deploymentKind}\n` +
|
|
297
676
|
` alias: ${aliasUrl}\n` +
|
|
298
|
-
` playground: ${
|
|
677
|
+
` playground: ${CLI} playground --prod --slug ${slug} --team ${ctx.teamId}\n`
|
|
299
678
|
);
|
|
300
679
|
return 0;
|
|
301
680
|
}
|
|
@@ -23,6 +23,7 @@ import { z } from "zod";
|
|
|
23
23
|
import { CLI_COMMAND_NAME } from "../distribution.js";
|
|
24
24
|
|
|
25
25
|
export const DEFAULT_CURSOR_BACKEND_URL = "https://api2.cursor.sh";
|
|
26
|
+
export const DEFAULT_CURSOR_EXTERNAL_API_URL = "https://api.cursor.com";
|
|
26
27
|
const DEFAULT_CURSOR_WEBSITE_URL = "https://cursor.com";
|
|
27
28
|
|
|
28
29
|
/** Backend API base URL (`CURSOR_API_BASE_URL` override for local dev). */
|
|
@@ -32,6 +33,19 @@ export function cursorBackendUrl(): string {
|
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* External API base URL for routes served only by the external-api
|
|
38
|
+
* microlith (`/internal/agent-serve/*`, engine aliases). In production
|
|
39
|
+
* these are mounted on api.cursor.com and 404 on api2.cursor.sh, so the
|
|
40
|
+
* deploy surface needs its own default. `CURSOR_API_BASE_URL` still
|
|
41
|
+
* overrides everything for local dev, where one server mounts all routes.
|
|
42
|
+
*/
|
|
43
|
+
export function cursorExternalApiUrl(): string {
|
|
44
|
+
return trimTrailingSlash(
|
|
45
|
+
process.env.CURSOR_API_BASE_URL ?? DEFAULT_CURSOR_EXTERNAL_API_URL
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
/** Website base URL for the login deep link (`CURSOR_WEBSITE_URL` override). */
|
|
36
50
|
export function cursorWebsiteUrl(): string {
|
|
37
51
|
return trimTrailingSlash(
|