@cursor/july 0.1.46 → 0.1.48
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 +30 -35
- package/dist/docs/404.html +1 -1
- package/dist/docs/ab.html +2 -2
- package/dist/docs/assets/{app.C2NFTb-h.js → app.BC1P20xI.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DV5SAHnQ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.o1_FJGip.js → VPLocalSearchBox.C_900xn2.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BcGduE7g.js → theme.CT135Jhh.js} +2 -2
- package/dist/docs/building-with-agents.html +2 -2
- package/dist/docs/concepts.html +2 -2
- package/dist/docs/deployment.html +2 -2
- package/dist/docs/evals.html +2 -2
- package/dist/docs/example-agents/approval-buddy.html +2 -2
- package/dist/docs/example-agents/benny.html +2 -2
- package/dist/docs/example-agents/bugbot.html +2 -2
- package/dist/docs/example-agents/codebase-wiki.html +2 -2
- package/dist/docs/example-agents/codeowners-review.html +2 -2
- package/dist/docs/example-agents/concierge.html +2 -2
- package/dist/docs/example-agents/fsd.html +2 -2
- package/dist/docs/example-agents/index.html +2 -2
- package/dist/docs/example-agents/knowledge-base.html +2 -2
- package/dist/docs/example-agents/oncall.html +2 -2
- package/dist/docs/example-agents/security-reviewer.html +2 -2
- package/dist/docs/example-agents/slack-agent.html +2 -2
- package/dist/docs/example-agents/weather-agent.html +2 -2
- package/dist/docs/guides/agent-to-agent.html +2 -2
- package/dist/docs/guides/cloud-runtime.html +2 -2
- package/dist/docs/guides/github.html +2 -2
- package/dist/docs/guides/human-in-the-loop.html +2 -2
- package/dist/docs/guides/mcp-oauth.html +2 -2
- package/dist/docs/guides/opentelemetry.html +2 -2
- package/dist/docs/guides/slack.html +2 -2
- package/dist/docs/guides/webhooks.html +2 -2
- package/dist/docs/hillclimbing.html +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/reference/agent-config.html +2 -2
- package/dist/docs/reference/artifacts.html +2 -2
- package/dist/docs/reference/channels.html +2 -2
- package/dist/docs/reference/cli.html +2 -2
- package/dist/docs/reference/connections.html +2 -2
- package/dist/docs/reference/hooks.html +2 -2
- package/dist/docs/reference/http-api.html +2 -2
- package/dist/docs/reference/instructions.html +2 -2
- package/dist/docs/reference/playground.html +2 -2
- package/dist/docs/reference/project-layout.html +2 -2
- package/dist/docs/reference/prompt.html +2 -2
- package/dist/docs/reference/schedules.html +2 -2
- package/dist/docs/reference/sessions.html +2 -2
- package/dist/docs/reference/skills.html +2 -2
- package/dist/docs/reference/subagents.html +2 -2
- package/dist/docs/reference/tools.html +2 -2
- package/dist/docs/scaffolding-agents.html +2 -2
- package/dist/docs/storage.html +2 -2
- package/dist/docs/templates/triage.html +2 -2
- package/dist/docs/troubleshooting.html +2 -2
- package/dist/internal/authored-loaders.d.ts +19 -0
- package/dist/internal/authored-loaders.d.ts.map +1 -1
- package/dist/internal/authored-loaders.js +39 -0
- package/dist/internal/cli-deploy.d.ts +2 -1
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +97 -1
- package/dist/internal/deploy-client.d.ts +7 -2
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +12 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +2 -8
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +3 -8
- package/dist/internal/logs-client.d.ts +1 -1
- package/dist/internal/logs-client.d.ts.map +1 -1
- package/dist/internal/logs-client.js +3 -0
- package/dist/internal/sdk-runner.d.ts +21 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +34 -1
- package/dist/internal/serve-restart.d.ts +42 -0
- package/dist/internal/serve-restart.d.ts.map +1 -0
- package/dist/internal/serve-restart.js +85 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +7 -2
- package/dist/playground/assets/index-CiCmH6R-.css +1 -0
- package/dist/playground/assets/{index-6wW6SAc4.js → index-DEVTyeOu.js} +46 -46
- package/dist/playground/index.html +2 -2
- package/package.json +1 -1
- package/src/bin/agent-serve.ts +39 -38
- package/src/internal/authored-loaders.ts +45 -0
- package/src/internal/cli-deploy.ts +103 -1
- package/src/internal/deploy-client.ts +19 -1
- package/src/internal/discovery.ts +5 -12
- package/src/internal/eval-runner.ts +3 -12
- package/src/internal/logs-client.ts +4 -0
- package/src/internal/sdk-runner.ts +46 -1
- package/src/internal/serve-restart.ts +120 -0
- package/src/internal/server.ts +11 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B-FxvuoI.js +0 -1
- package/dist/playground/assets/index-CSRYDAFw.css +0 -1
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
/>
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
10
10
|
<title>agent-serve playground</title>
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-DEVTyeOu.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CiCmH6R-.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"repository": {
|
package/src/bin/agent-serve.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* agent-serve slack <subcommand>
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { spawn } from "node:child_process";
|
|
19
20
|
import { readFile } from "node:fs/promises";
|
|
20
21
|
import { resolve } from "node:path";
|
|
21
22
|
import { parseArgs } from "node:util";
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
cmdTrajectory,
|
|
45
46
|
} from "../internal/cli-ax.js";
|
|
46
47
|
import {
|
|
48
|
+
cmdDelete,
|
|
47
49
|
cmdDeploy,
|
|
48
50
|
cmdDeploymentStatus,
|
|
49
51
|
cmdDeployments,
|
|
@@ -61,6 +63,11 @@ import {
|
|
|
61
63
|
packageVersion,
|
|
62
64
|
} from "../internal/distribution.js";
|
|
63
65
|
import { loadAgentLocalEnv } from "../internal/local-env.js";
|
|
66
|
+
import {
|
|
67
|
+
ENTER_RESTART_EXIT_CODE,
|
|
68
|
+
isServeRestartWorker,
|
|
69
|
+
runEnterRestartSupervisor,
|
|
70
|
+
} from "../internal/serve-restart.js";
|
|
64
71
|
import { clearTerminal, stdoutPalette } from "../internal/terminal-style.js";
|
|
65
72
|
import { cmdUpdate, warnIfOutdated } from "../internal/update-check.js";
|
|
66
73
|
import type { AgentProject, AgentServeHandle } from "../types.js";
|
|
@@ -134,6 +141,7 @@ Usage:
|
|
|
134
141
|
${CLI} deployments [--team <id>] [--json]
|
|
135
142
|
${CLI} deployment <slug> [--team <id>] [--json]
|
|
136
143
|
${CLI} stop <slug> [--team <id>] [--json] [--no-wait]
|
|
144
|
+
${CLI} delete <slug> [--team <id>] [--json] [--no-wait]
|
|
137
145
|
${CLI} rotate-token <slug> [--team <id>]
|
|
138
146
|
${CLI} rotate-pod-credential <slug> [--team <id>]
|
|
139
147
|
${CLI} secrets set <slug> NAME... | list <slug> | unset <slug> NAME
|
|
@@ -143,7 +151,8 @@ Usage:
|
|
|
143
151
|
|
|
144
152
|
Commands:
|
|
145
153
|
serve Serve agents under /<slug>/... (index at /). On a TTY, press
|
|
146
|
-
Enter to restart (
|
|
154
|
+
Enter to restart (re-executes this process so agent code, evals,
|
|
155
|
+
and Vite reload).
|
|
147
156
|
dev Local development: same as \`${CLI} serve --dev\` (manual
|
|
148
157
|
schedule/reminder dispatch, unsigned GitHub loopback, Vite HMR).
|
|
149
158
|
chat Interactive REPL against a running server (follow-ups keep the
|
|
@@ -252,6 +261,8 @@ Commands:
|
|
|
252
261
|
deployments List the team's deployments (slug, status, generation, kind).
|
|
253
262
|
deployment Show one deployment in full (status, lastError, engine info).
|
|
254
263
|
stop Stop a deployment and wait until it reports stopped.
|
|
264
|
+
delete Delete a deployment (teardown + purge the record) and wait
|
|
265
|
+
until GET 404s. The slug is then free to redeploy.
|
|
255
266
|
rotate-token Mint a new alias token for a deployment (shown once); the old
|
|
256
267
|
token stops working immediately.
|
|
257
268
|
rotate-pod-credential
|
|
@@ -555,7 +566,12 @@ async function main(argv: string[]): Promise<number> {
|
|
|
555
566
|
// Best-effort, cached, and silent on failure. `update` does its own fresh
|
|
556
567
|
// lookup, --json runs stay quiet for machine consumers, and `mcp` skips it
|
|
557
568
|
// so MCP client spawns stay fast (stdout there is the protocol wire).
|
|
558
|
-
if (
|
|
569
|
+
if (
|
|
570
|
+
command !== "update" &&
|
|
571
|
+
command !== "mcp" &&
|
|
572
|
+
values.json !== true &&
|
|
573
|
+
!isServeRestartWorker()
|
|
574
|
+
) {
|
|
559
575
|
await warnIfOutdated();
|
|
560
576
|
}
|
|
561
577
|
|
|
@@ -731,6 +747,10 @@ async function main(argv: string[]): Promise<number> {
|
|
|
731
747
|
return cmdStop(positionals[0], deployOptionsFromValues(values));
|
|
732
748
|
}
|
|
733
749
|
|
|
750
|
+
case "delete": {
|
|
751
|
+
return cmdDelete(positionals[0], deployOptionsFromValues(values));
|
|
752
|
+
}
|
|
753
|
+
|
|
734
754
|
case "rotate-token": {
|
|
735
755
|
return cmdRotateToken(positionals[0], deployOptionsFromValues(values));
|
|
736
756
|
}
|
|
@@ -1227,6 +1247,16 @@ async function main(argv: string[]): Promise<number> {
|
|
|
1227
1247
|
},
|
|
1228
1248
|
};
|
|
1229
1249
|
const enterToRestart = process.stdin.isTTY === true;
|
|
1250
|
+
if (enterToRestart && !isServeRestartWorker()) {
|
|
1251
|
+
return runEnterRestartSupervisor({
|
|
1252
|
+
spawn: (command, args, spawnOptions) =>
|
|
1253
|
+
spawn(command, args, spawnOptions),
|
|
1254
|
+
onRestart: () => {
|
|
1255
|
+
clearTerminal();
|
|
1256
|
+
},
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1230
1260
|
const bannerOpts = {
|
|
1231
1261
|
noPlayground: values["no-playground"] === true,
|
|
1232
1262
|
dev,
|
|
@@ -1244,9 +1274,9 @@ async function main(argv: string[]): Promise<number> {
|
|
|
1244
1274
|
return next;
|
|
1245
1275
|
};
|
|
1246
1276
|
|
|
1247
|
-
|
|
1277
|
+
const handle = await start();
|
|
1248
1278
|
let closing = false;
|
|
1249
|
-
let
|
|
1279
|
+
let restartRequested = false;
|
|
1250
1280
|
|
|
1251
1281
|
await new Promise<void>((resolve) => {
|
|
1252
1282
|
const shutdown = (): void => {
|
|
@@ -1268,40 +1298,17 @@ async function main(argv: string[]): Promise<number> {
|
|
|
1268
1298
|
process.stdin.on("data", (chunk: string | Buffer) => {
|
|
1269
1299
|
const text =
|
|
1270
1300
|
typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
1271
|
-
if (!isEnterOnly(text) || closing
|
|
1301
|
+
if (!isEnterOnly(text) || closing) {
|
|
1272
1302
|
return;
|
|
1273
1303
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
// Hold the "restarting…" frame briefly so a fast close doesn't look
|
|
1277
|
-
// like Enter did nothing.
|
|
1304
|
+
closing = true;
|
|
1305
|
+
restartRequested = true;
|
|
1278
1306
|
clearTerminal();
|
|
1279
1307
|
process.stdout.write(style.dim("restarting…\n"));
|
|
1280
|
-
void
|
|
1281
|
-
.then(() => {
|
|
1282
|
-
clearTerminal();
|
|
1283
|
-
return start();
|
|
1284
|
-
})
|
|
1285
|
-
.then((next) => {
|
|
1286
|
-
handle = next;
|
|
1287
|
-
})
|
|
1288
|
-
.catch((error: unknown) => {
|
|
1289
|
-
process.stderr.write(
|
|
1290
|
-
`[agent-sdk] restart failed: ${
|
|
1291
|
-
error instanceof Error
|
|
1292
|
-
? (error.stack ?? error.message)
|
|
1293
|
-
: String(error)
|
|
1294
|
-
}\n`
|
|
1295
|
-
);
|
|
1296
|
-
closing = true;
|
|
1297
|
-
resolve();
|
|
1298
|
-
})
|
|
1299
|
-
.finally(() => {
|
|
1300
|
-
restarting = false;
|
|
1301
|
-
});
|
|
1308
|
+
void handle.close().finally(resolve);
|
|
1302
1309
|
});
|
|
1303
1310
|
});
|
|
1304
|
-
return 0;
|
|
1311
|
+
return restartRequested ? ENTER_RESTART_EXIT_CODE : 0;
|
|
1305
1312
|
}
|
|
1306
1313
|
|
|
1307
1314
|
default: {
|
|
@@ -1437,12 +1444,6 @@ function isEnterOnly(text: string): boolean {
|
|
|
1437
1444
|
return text.length > 0 && [...text].every((ch) => ch === "\n" || ch === "\r");
|
|
1438
1445
|
}
|
|
1439
1446
|
|
|
1440
|
-
function sleep(ms: number): Promise<void> {
|
|
1441
|
-
return new Promise((resolve) => {
|
|
1442
|
-
setTimeout(resolve, ms);
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
1447
|
/**
|
|
1447
1448
|
* `dev [path]` takes a positional project root; `serve` keeps `--dir` only.
|
|
1448
1449
|
* Reject mixing a positional with an explicit `--dir`, and extra args on `dev`.
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* them.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { stat } from "node:fs/promises";
|
|
19
20
|
import { register as registerModuleHooks } from "node:module";
|
|
20
21
|
import { join } from "node:path";
|
|
21
22
|
import { pathToFileURL } from "node:url";
|
|
@@ -38,6 +39,50 @@ import {
|
|
|
38
39
|
const TSX_REGISTERED_KEY = Symbol.for("cursor.agentkit.tsxRegistered");
|
|
39
40
|
const ALIASES_REGISTERED_KEY = Symbol.for("cursor.agentkit.aliasesRegistered");
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* ESM specifier for an authored agent/eval file.
|
|
44
|
+
*
|
|
45
|
+
* Node caches `import()` by URL for the life of the process, so a long-lived
|
|
46
|
+
* `agent-sdk dev` would otherwise keep serving the first load of eval files
|
|
47
|
+
* and agent modules. The mtime query string is ignored by the filesystem
|
|
48
|
+
* resolver and is the cache key.
|
|
49
|
+
*
|
|
50
|
+
* Truncate to an integer: vitest/esbuild treat the substring after the last
|
|
51
|
+
* `.` as a loader id, so `?mtime=1700000000000.4949` fails with
|
|
52
|
+
* `Invalid loader value: "4949"`. Named `mtime` rather than `t` so Vite does
|
|
53
|
+
* not treat it as its HMR timestamp and collapse the specifier to the path.
|
|
54
|
+
*/
|
|
55
|
+
export function authoredModuleSpecifier(
|
|
56
|
+
absolutePath: string,
|
|
57
|
+
mtimeMs: number
|
|
58
|
+
): string {
|
|
59
|
+
const url = pathToFileURL(absolutePath);
|
|
60
|
+
url.searchParams.set("mtime", String(Math.trunc(mtimeMs)));
|
|
61
|
+
return url.href;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Dynamically import an authored TypeScript/JavaScript module, re-reading it
|
|
66
|
+
* from disk when the file's mtime has changed since the last import.
|
|
67
|
+
*/
|
|
68
|
+
export async function importAuthoredModule(
|
|
69
|
+
absolutePath: string
|
|
70
|
+
): Promise<Record<string, unknown>> {
|
|
71
|
+
let mtimeMs = 0;
|
|
72
|
+
try {
|
|
73
|
+
mtimeMs = (await stat(absolutePath)).mtimeMs;
|
|
74
|
+
} catch {
|
|
75
|
+
// import() reports the real missing-file error.
|
|
76
|
+
}
|
|
77
|
+
const loaded: unknown = await import(
|
|
78
|
+
authoredModuleSpecifier(absolutePath, mtimeMs)
|
|
79
|
+
);
|
|
80
|
+
if (typeof loaded !== "object" || loaded === null) {
|
|
81
|
+
throw new Error(`authored module is not an object: ${absolutePath}`);
|
|
82
|
+
}
|
|
83
|
+
return loaded as Record<string, unknown>;
|
|
84
|
+
}
|
|
85
|
+
|
|
41
86
|
let registration: Promise<void> | undefined;
|
|
42
87
|
|
|
43
88
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agent-sdk deploy | deployments | deployment | stop | rotate-token |
|
|
2
|
+
* `agent-sdk deploy | deployments | deployment | stop | delete | rotate-token |
|
|
3
3
|
* rotate-pod-credential | secrets set|list|unset` — Heroku-style deploys
|
|
4
4
|
* onto Cursor's managed agent-serve hosting.
|
|
5
5
|
*
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
|
|
25
25
|
import {
|
|
26
26
|
AgentServeDeployClient,
|
|
27
|
+
type DeleteAccepted,
|
|
27
28
|
type DeployAccepted,
|
|
28
29
|
DeployApiError,
|
|
29
30
|
type Deployment,
|
|
@@ -1117,6 +1118,54 @@ export async function cmdStop(
|
|
|
1117
1118
|
return 0;
|
|
1118
1119
|
}
|
|
1119
1120
|
|
|
1121
|
+
// ============================================================================
|
|
1122
|
+
// delete <slug>
|
|
1123
|
+
// ============================================================================
|
|
1124
|
+
|
|
1125
|
+
export async function cmdDelete(
|
|
1126
|
+
slug: string | undefined,
|
|
1127
|
+
options: DeployCliOptions
|
|
1128
|
+
): Promise<number> {
|
|
1129
|
+
if (slug === undefined || slug === "") {
|
|
1130
|
+
(options.err ?? ((t: string) => void process.stderr.write(t)))(
|
|
1131
|
+
`Usage: ${CLI} delete <slug> [--team <id>] [--json] [--no-wait]\n`
|
|
1132
|
+
);
|
|
1133
|
+
return 1;
|
|
1134
|
+
}
|
|
1135
|
+
const ctx = await prepare(options);
|
|
1136
|
+
if (typeof ctx === "number") {
|
|
1137
|
+
return ctx;
|
|
1138
|
+
}
|
|
1139
|
+
let accepted: DeleteAccepted;
|
|
1140
|
+
try {
|
|
1141
|
+
accepted = await ctx.client.deleteDeployment({
|
|
1142
|
+
teamId: ctx.teamId,
|
|
1143
|
+
slug,
|
|
1144
|
+
});
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
return renderApiError(error, ctx.err);
|
|
1147
|
+
}
|
|
1148
|
+
if (ctx.noWait) {
|
|
1149
|
+
if (ctx.json) {
|
|
1150
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1151
|
+
} else {
|
|
1152
|
+
ctx.out(`Delete accepted for ${slug} (status: ${accepted.status}).\n`);
|
|
1153
|
+
}
|
|
1154
|
+
return 0;
|
|
1155
|
+
}
|
|
1156
|
+
try {
|
|
1157
|
+
await pollUntilGone(ctx, slug);
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
return renderApiError(error, ctx.err);
|
|
1160
|
+
}
|
|
1161
|
+
if (ctx.json) {
|
|
1162
|
+
ctx.out(`${JSON.stringify({ slug, status: "deleted" }, null, 2)}\n`);
|
|
1163
|
+
} else {
|
|
1164
|
+
ctx.out(`Deleted ${slug} (team ${ctx.teamId}).\n`);
|
|
1165
|
+
}
|
|
1166
|
+
return 0;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1120
1169
|
// ============================================================================
|
|
1121
1170
|
// rotate-token <slug>
|
|
1122
1171
|
// ============================================================================
|
|
@@ -1526,6 +1575,59 @@ async function pollUntil(
|
|
|
1526
1575
|
}
|
|
1527
1576
|
}
|
|
1528
1577
|
|
|
1578
|
+
/**
|
|
1579
|
+
* Wait until GET 404s (the delete reconciler purged the record). Treats
|
|
1580
|
+
* other errors as failures. Throws DeployApiError on timeout.
|
|
1581
|
+
*/
|
|
1582
|
+
async function pollUntilGone(
|
|
1583
|
+
ctx: DeployCliContext,
|
|
1584
|
+
slug: string
|
|
1585
|
+
): Promise<void> {
|
|
1586
|
+
const startedAt = Date.now();
|
|
1587
|
+
const tty = process.stderr.isTTY === true;
|
|
1588
|
+
let sawProgress = false;
|
|
1589
|
+
const report = (label: string): void => {
|
|
1590
|
+
if (ctx.json) {
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1000);
|
|
1594
|
+
if (tty) {
|
|
1595
|
+
ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
|
|
1596
|
+
sawProgress = true;
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
const finishProgressLine = (): void => {
|
|
1600
|
+
if (tty && sawProgress) {
|
|
1601
|
+
ctx.err("\n");
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
for (;;) {
|
|
1606
|
+
try {
|
|
1607
|
+
const detail = await ctx.client.getDeployment({
|
|
1608
|
+
teamId: ctx.teamId,
|
|
1609
|
+
slug,
|
|
1610
|
+
});
|
|
1611
|
+
report(deployProgressLabel(detail));
|
|
1612
|
+
} catch (error: unknown) {
|
|
1613
|
+
if (error instanceof DeployApiError && error.status === 404) {
|
|
1614
|
+
finishProgressLine();
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
finishProgressLine();
|
|
1618
|
+
throw error;
|
|
1619
|
+
}
|
|
1620
|
+
if (Date.now() - startedAt >= ctx.timeoutMs) {
|
|
1621
|
+
finishProgressLine();
|
|
1622
|
+
throw new DeployApiError(
|
|
1623
|
+
`Timed out after ${Math.round(ctx.timeoutMs / 1000)}s waiting for ${slug} to disappear. It may still be deleting — check \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.`,
|
|
1624
|
+
0
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
await sleep(ctx.pollIntervalMs);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1529
1631
|
function aliasTokenBlock(aliasToken: string): string {
|
|
1530
1632
|
return (
|
|
1531
1633
|
`\n Alias token (shown ONCE — save it now, it cannot be retrieved again):\n\n` +
|
|
@@ -25,7 +25,8 @@ export type DeploymentStatus =
|
|
|
25
25
|
| "degraded"
|
|
26
26
|
| "failed"
|
|
27
27
|
| "stopping"
|
|
28
|
-
| "stopped"
|
|
28
|
+
| "stopped"
|
|
29
|
+
| "deleting";
|
|
29
30
|
|
|
30
31
|
/** Customer-safe deploy progress (no hosting phase / infra names). */
|
|
31
32
|
export type DeploymentProgress =
|
|
@@ -39,6 +40,7 @@ export type DeploymentProgress =
|
|
|
39
40
|
| "failed"
|
|
40
41
|
| "stopping"
|
|
41
42
|
| "stopped"
|
|
43
|
+
| "deleting"
|
|
42
44
|
| "degraded";
|
|
43
45
|
|
|
44
46
|
export const TERMINAL_DEPLOY_STATUSES: ReadonlySet<DeploymentStatus> = new Set([
|
|
@@ -211,6 +213,8 @@ export interface StopAccepted {
|
|
|
211
213
|
status: string;
|
|
212
214
|
}
|
|
213
215
|
|
|
216
|
+
export type DeleteAccepted = StopAccepted;
|
|
217
|
+
|
|
214
218
|
/** One secret's metadata from the list endpoint — names only, never values. */
|
|
215
219
|
export interface SecretInfo {
|
|
216
220
|
name: string;
|
|
@@ -276,6 +280,7 @@ const deploymentSchema = z.object({
|
|
|
276
280
|
"failed",
|
|
277
281
|
"stopping",
|
|
278
282
|
"stopped",
|
|
283
|
+
"deleting",
|
|
279
284
|
]),
|
|
280
285
|
progress: z
|
|
281
286
|
.enum([
|
|
@@ -289,6 +294,7 @@ const deploymentSchema = z.object({
|
|
|
289
294
|
"failed",
|
|
290
295
|
"stopping",
|
|
291
296
|
"stopped",
|
|
297
|
+
"deleting",
|
|
292
298
|
"degraded",
|
|
293
299
|
])
|
|
294
300
|
.optional(),
|
|
@@ -495,6 +501,18 @@ export class AgentServeDeployClient {
|
|
|
495
501
|
return stopAcceptedSchema.parse(raw);
|
|
496
502
|
}
|
|
497
503
|
|
|
504
|
+
async deleteDeployment(args: {
|
|
505
|
+
teamId: number;
|
|
506
|
+
slug: string;
|
|
507
|
+
}): Promise<DeleteAccepted> {
|
|
508
|
+
const raw = await this.request({
|
|
509
|
+
method: "DELETE",
|
|
510
|
+
path: `/internal/agent-serve/deployments/${encodeURIComponent(args.slug)}?teamId=${args.teamId}`,
|
|
511
|
+
context: { verb: "delete", slug: args.slug },
|
|
512
|
+
});
|
|
513
|
+
return stopAcceptedSchema.parse(raw);
|
|
514
|
+
}
|
|
515
|
+
|
|
498
516
|
/** Returns the new alias token exactly once; the old one dies now. */
|
|
499
517
|
async rotateAliasToken(args: {
|
|
500
518
|
teamId: number;
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
relative,
|
|
17
17
|
resolve,
|
|
18
18
|
} from "node:path";
|
|
19
|
-
import { pathToFileURL } from "node:url";
|
|
20
19
|
import { type ABConfigFile, resolveABMaxPlaygroundSessions } from "../ab.js";
|
|
21
20
|
import { resolveArtifactsMax } from "../artifacts.js";
|
|
22
21
|
import { httpChannel } from "../channels.js";
|
|
@@ -61,7 +60,10 @@ import {
|
|
|
61
60
|
type ToolDefinition,
|
|
62
61
|
toPublicCloudInfo,
|
|
63
62
|
} from "../types.js";
|
|
64
|
-
import {
|
|
63
|
+
import {
|
|
64
|
+
importAuthoredModule,
|
|
65
|
+
registerAuthoredModuleLoaders,
|
|
66
|
+
} from "./authored-loaders.js";
|
|
65
67
|
import { normalizePositiveInt } from "./bounded-int.js";
|
|
66
68
|
import { getDefinitionKind } from "./brand.js";
|
|
67
69
|
import {
|
|
@@ -94,15 +96,6 @@ export interface LoadAgentProjectOptions {
|
|
|
94
96
|
|
|
95
97
|
type ImportModule = (absolutePath: string) => Promise<Record<string, unknown>>;
|
|
96
98
|
|
|
97
|
-
async function defaultImportModule(
|
|
98
|
-
absolutePath: string
|
|
99
|
-
): Promise<Record<string, unknown>> {
|
|
100
|
-
return (await import(pathToFileURL(absolutePath).href)) as Record<
|
|
101
|
-
string,
|
|
102
|
-
unknown
|
|
103
|
-
>;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
99
|
interface DiscoveryContext {
|
|
107
100
|
rootDir: string;
|
|
108
101
|
diagnostics: Diagnostic[];
|
|
@@ -282,7 +275,7 @@ export async function loadAgentProject(
|
|
|
282
275
|
const ctx: DiscoveryContext = {
|
|
283
276
|
rootDir,
|
|
284
277
|
diagnostics,
|
|
285
|
-
importModule: options.importModule ??
|
|
278
|
+
importModule: options.importModule ?? importAuthoredModule,
|
|
286
279
|
};
|
|
287
280
|
|
|
288
281
|
const name = (await readPackageName(rootDir)) ?? basename(rootDir);
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import type { Dirent } from "node:fs";
|
|
12
12
|
import { readdir } from "node:fs/promises";
|
|
13
13
|
import { join, relative, resolve } from "node:path";
|
|
14
|
-
import { pathToFileURL } from "node:url";
|
|
15
14
|
import type {
|
|
16
15
|
EvalAssertionHandle,
|
|
17
16
|
EvalAssertions,
|
|
@@ -62,6 +61,7 @@ import {
|
|
|
62
61
|
normalizeEvalMaxConcurrency,
|
|
63
62
|
} from "../evals.js";
|
|
64
63
|
import type { ModelSetting, SessionEvent } from "../types.js";
|
|
64
|
+
import { importAuthoredModule } from "./authored-loaders.js";
|
|
65
65
|
import { isDefinitionOfKind } from "./brand.js";
|
|
66
66
|
import {
|
|
67
67
|
callJudgeModel,
|
|
@@ -131,15 +131,6 @@ export interface RunEvalsOptions extends EvalSelectionOptions {
|
|
|
131
131
|
signal?: AbortSignal;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
async function defaultImport(
|
|
135
|
-
absolutePath: string
|
|
136
|
-
): Promise<Record<string, unknown>> {
|
|
137
|
-
return (await import(pathToFileURL(absolutePath).href)) as Record<
|
|
138
|
-
string,
|
|
139
|
-
unknown
|
|
140
|
-
>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
134
|
async function listEvalFiles(dir: string): Promise<string[]> {
|
|
144
135
|
const out: string[] = [];
|
|
145
136
|
async function walk(current: string): Promise<void> {
|
|
@@ -172,7 +163,7 @@ export async function discoverEvals(
|
|
|
172
163
|
projectRoot: string,
|
|
173
164
|
importModule: (
|
|
174
165
|
absolutePath: string
|
|
175
|
-
) => Promise<Record<string, unknown>> =
|
|
166
|
+
) => Promise<Record<string, unknown>> = importAuthoredModule
|
|
176
167
|
): Promise<{
|
|
177
168
|
evals: DiscoveredEval[];
|
|
178
169
|
config: Partial<EvalConfigFile>;
|
|
@@ -291,7 +282,7 @@ export async function runDiscoveredEvals(
|
|
|
291
282
|
selected = options.discovered.evals;
|
|
292
283
|
config = options.discovered.config;
|
|
293
284
|
} else {
|
|
294
|
-
const importModule = options.importModule ??
|
|
285
|
+
const importModule = options.importModule ?? importAuthoredModule;
|
|
295
286
|
const discovered = await discoverEvals(options.projectRoot, importModule);
|
|
296
287
|
config = discovered.config;
|
|
297
288
|
selected = filterDiscoveredEvals(discovered.evals, {
|
|
@@ -43,6 +43,7 @@ export type LogsProgress =
|
|
|
43
43
|
| "failed"
|
|
44
44
|
| "stopping"
|
|
45
45
|
| "stopped"
|
|
46
|
+
| "deleting"
|
|
46
47
|
| "degraded";
|
|
47
48
|
|
|
48
49
|
const PROGRESS_MESSAGES: Readonly<Record<LogsProgress, string>> = {
|
|
@@ -56,6 +57,7 @@ const PROGRESS_MESSAGES: Readonly<Record<LogsProgress, string>> = {
|
|
|
56
57
|
failed: "Deploy failed",
|
|
57
58
|
stopping: "Stopping deployment…",
|
|
58
59
|
stopped: "Deployment stopped",
|
|
60
|
+
deleting: "Deleting deployment…",
|
|
59
61
|
degraded: "Deployment degraded",
|
|
60
62
|
};
|
|
61
63
|
|
|
@@ -385,6 +387,8 @@ function resolveProgress(detail: DeploymentDetail): LogsProgress {
|
|
|
385
387
|
return "stopping";
|
|
386
388
|
case "stopped":
|
|
387
389
|
return "stopped";
|
|
390
|
+
case "deleting":
|
|
391
|
+
return "deleting";
|
|
388
392
|
default:
|
|
389
393
|
return "queued";
|
|
390
394
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type AgentDefinition,
|
|
13
13
|
type AgentOptions,
|
|
14
14
|
type CloudAgentOptions,
|
|
15
|
+
Cursor,
|
|
15
16
|
createAgentPlatform,
|
|
16
17
|
JsonlLocalAgentStore,
|
|
17
18
|
type LocalAgentStore,
|
|
@@ -75,6 +76,36 @@ type MaybePrewarmingPlatform = {
|
|
|
75
76
|
) => Promise<() => Promise<void>>;
|
|
76
77
|
};
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* How long a non-`--dev` serve holds workspace scans (rules, skills,
|
|
81
|
+
* `AGENTS.md`, ignore files). The SDK default is 20s for an editor whose
|
|
82
|
+
* files move under it; agent-serve checkouts only change on deploy, so a
|
|
83
|
+
* missed mid-process edit is the right trade. `CURSOR_RIPWALK_CACHE_TTL_MS`
|
|
84
|
+
* still wins when set. `--dev` leaves the 20s default so skill edits show up.
|
|
85
|
+
*/
|
|
86
|
+
export const SERVE_WORKSPACE_SCAN_CACHE_TTL_MS: number = 24 * 60 * 60 * 1000;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the SDK workspace-scan TTL for this serve. `undefined` means "do
|
|
90
|
+
* not call `Cursor.configure`" and leave the SDK at its 20s editor default.
|
|
91
|
+
*/
|
|
92
|
+
export function resolveServeWorkspaceScanCacheTtlMs(args: {
|
|
93
|
+
dev: boolean;
|
|
94
|
+
env?: NodeJS.ProcessEnv;
|
|
95
|
+
}): number | undefined {
|
|
96
|
+
const raw = (args.env ?? process.env).CURSOR_RIPWALK_CACHE_TTL_MS?.trim();
|
|
97
|
+
if (raw !== undefined && raw !== "") {
|
|
98
|
+
const parsed = Number(raw);
|
|
99
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
100
|
+
return parsed;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (args.dev) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
return SERVE_WORKSPACE_SCAN_CACHE_TTL_MS;
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
export interface CursorSdkRunnerOptions {
|
|
79
110
|
/** Cursor API key. Defaults to the `CURSOR_API_KEY` environment variable. */
|
|
80
111
|
apiKey?: string;
|
|
@@ -86,6 +117,11 @@ export interface CursorSdkRunnerOptions {
|
|
|
86
117
|
store?: LocalAgentStore;
|
|
87
118
|
/** Diagnostics for degraded paths (resume-miss fallback). */
|
|
88
119
|
logger?: (line: string) => void;
|
|
120
|
+
/**
|
|
121
|
+
* Hold workspace scans this long (ms). Applied via `Cursor.configure` so
|
|
122
|
+
* each new local executor reuses the walk. Omit to keep the SDK 20s default.
|
|
123
|
+
*/
|
|
124
|
+
workspaceScanCacheTtlMs?: number;
|
|
89
125
|
}
|
|
90
126
|
|
|
91
127
|
/**
|
|
@@ -128,7 +164,16 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
128
164
|
private readonly stores = new Map<string, Promise<LocalAgentStore>>();
|
|
129
165
|
private readonly prewarmReleases: Array<() => Promise<void>> = [];
|
|
130
166
|
|
|
131
|
-
constructor(private readonly options: CursorSdkRunnerOptions = {}) {
|
|
167
|
+
constructor(private readonly options: CursorSdkRunnerOptions = {}) {
|
|
168
|
+
if (options.workspaceScanCacheTtlMs !== undefined) {
|
|
169
|
+
Cursor.configure({
|
|
170
|
+
local: { workspaceScanCacheTtlMs: options.workspaceScanCacheTtlMs },
|
|
171
|
+
});
|
|
172
|
+
this.log(
|
|
173
|
+
`[agent-sdk] workspace scan cache ttl=${options.workspaceScanCacheTtlMs}ms`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
132
177
|
|
|
133
178
|
async runTurn(request: RunnerTurnRequest): Promise<TurnOutcome> {
|
|
134
179
|
let sawUpdate = false;
|