@cursor/july 0.1.47 → 0.1.49
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 +2 -2
- package/dist/docs/ab.html +5 -5
- package/dist/docs/assets/{ab.md.hdxsvX5M.js → ab.md.DYjwREAP.js} +2 -2
- package/dist/docs/assets/{ab.md.hdxsvX5M.lean.js → ab.md.DYjwREAP.lean.js} +1 -1
- package/dist/docs/assets/{app.IEXEim5m.js → app.BpWAgjaR.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.D00d772K.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.78s6nqsH.js → VPLocalSearchBox.CnAktZtG.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DroGdz_Q.js → theme.B5IvUxOB.js} +2 -2
- package/dist/docs/assets/{evals.md.qeoGgPw0.js → evals.md.CVe_O75-.js} +3 -3
- package/dist/docs/assets/{evals.md.qeoGgPw0.lean.js → evals.md.CVe_O75-.lean.js} +1 -1
- package/dist/docs/assets/{reference_http-api.md.DGrw_wOu.js → reference_http-api.md.DMbdFGVQ.js} +1 -1
- package/dist/docs/assets/{storage.md.CJYd5YmN.js → storage.md.D8NQ389l.js} +3 -3
- package/dist/docs/assets/storage.md.D8NQ389l.lean.js +1 -0
- 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 +6 -6
- 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/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 +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +5 -5
- 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 +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +5 -5
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- 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/cli-slack.js +0 -3
- 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/event-log.d.ts +17 -0
- package/dist/internal/event-log.d.ts.map +1 -1
- package/dist/internal/event-log.js +32 -2
- 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/process-memory-telemetry.d.ts +90 -0
- package/dist/internal/process-memory-telemetry.d.ts.map +1 -0
- package/dist/internal/process-memory-telemetry.js +259 -0
- 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 +9 -10
- package/dist/internal/session-engine.d.ts +4 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +19 -5
- package/dist/internal/slack-provision-client.d.ts +4 -4
- package/dist/internal/slack-provision-client.js +7 -7
- package/dist/playground/assets/index-Dox74yWJ.css +1 -0
- package/dist/playground/assets/{index-BgUHfe3L.js → index-Dq_HqyU-.js} +46 -46
- package/dist/playground/index.html +2 -2
- package/dist/storage.d.ts +36 -10
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +82 -6
- package/docs/ab.md +4 -3
- package/docs/evals.md +10 -7
- package/docs/reference/http-api.md +4 -3
- package/docs/storage.md +38 -22
- package/package.json +1 -1
- package/skills/setup-slack/SKILL.md +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/cli-slack.ts +0 -5
- 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/event-log.ts +45 -7
- package/src/internal/logs-client.ts +4 -0
- package/src/internal/process-memory-telemetry.ts +373 -0
- package/src/internal/serve-restart.ts +120 -0
- package/src/internal/server.ts +13 -0
- package/src/internal/session-engine.ts +20 -1
- package/src/internal/slack-provision-client.ts +7 -7
- package/src/storage.ts +130 -11
- package/dist/docs/assets/chunks/@localSearchIndexroot.D7w61u6Y.js +0 -1
- package/dist/docs/assets/storage.md.CJYd5YmN.lean.js +0 -1
- package/dist/playground/assets/index-uFaeqVZS.css +0 -1
- /package/dist/docs/assets/{reference_http-api.md.DGrw_wOu.lean.js → reference_http-api.md.DMbdFGVQ.lean.js} +0 -0
|
@@ -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` +
|
|
@@ -1126,11 +1126,6 @@ function renderCreateProvisionError(
|
|
|
1126
1126
|
`${error.message}\nA concurrent \`${CLI} slack create\` for this agent won the race; re-run create to pick up that app.\n`
|
|
1127
1127
|
);
|
|
1128
1128
|
return 1;
|
|
1129
|
-
case "app_cap_reached":
|
|
1130
|
-
err(
|
|
1131
|
-
`${error.message}\nDelete unused apps with \`${CLI} slack destroy\`, then re-run create.\n`
|
|
1132
|
-
);
|
|
1133
|
-
return 1;
|
|
1134
1129
|
case "ratelimited":
|
|
1135
1130
|
err(`${error.message}\nRe-run \`${CLI} slack create\` after the wait.\n`);
|
|
1136
1131
|
return 1;
|
|
@@ -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, {
|
|
@@ -59,6 +59,17 @@ export class SessionEventLog {
|
|
|
59
59
|
return this.events.length;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* In-process residency vs recorded length. Today those are the same
|
|
64
|
+
* (`events` is the cache). A later change can drop the cache and keep
|
|
65
|
+
* `eventsTotal` as the durable count while `eventsRetained` shrinks to
|
|
66
|
+
* the unflushed tail.
|
|
67
|
+
*/
|
|
68
|
+
memoryStats(): { eventsRetained: number; eventsTotal: number } {
|
|
69
|
+
const n = this.events.length;
|
|
70
|
+
return { eventsRetained: n, eventsTotal: n };
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
/**
|
|
63
74
|
* Assign the next index, record the event, and fan out to live
|
|
64
75
|
* subscribers. Ordering is synchronous; file persistence is chained in
|
|
@@ -154,9 +165,16 @@ export class SessionEventLog {
|
|
|
154
165
|
}
|
|
155
166
|
}
|
|
156
167
|
|
|
168
|
+
export type EventLogMemoryStats = {
|
|
169
|
+
residentSessions: number;
|
|
170
|
+
eventsRetained: number;
|
|
171
|
+
eventsTotal: number;
|
|
172
|
+
};
|
|
173
|
+
|
|
157
174
|
/** Lazily opens one {@link SessionEventLog} per session. */
|
|
158
175
|
export class EventLogStore {
|
|
159
176
|
private readonly logs = new Map<string, Promise<SessionEventLog>>();
|
|
177
|
+
private readonly opened = new Map<string, SessionEventLog>();
|
|
160
178
|
|
|
161
179
|
constructor(
|
|
162
180
|
private readonly eventFilePathFor: (sessionId: string) => string,
|
|
@@ -170,18 +188,38 @@ export class EventLogStore {
|
|
|
170
188
|
let log = this.logs.get(sessionId);
|
|
171
189
|
if (log === undefined) {
|
|
172
190
|
const filePath = this.eventFilePathFor(sessionId);
|
|
173
|
-
log = mkdir(dirname(filePath), { recursive: true })
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
191
|
+
log = mkdir(dirname(filePath), { recursive: true })
|
|
192
|
+
.then(() =>
|
|
193
|
+
SessionEventLog.open({
|
|
194
|
+
sessionId,
|
|
195
|
+
filePath,
|
|
196
|
+
onPersistError: (error) => this.onPersistError(sessionId, error),
|
|
197
|
+
})
|
|
198
|
+
)
|
|
199
|
+
.then((opened) => {
|
|
200
|
+
this.opened.set(sessionId, opened);
|
|
201
|
+
return opened;
|
|
202
|
+
});
|
|
180
203
|
this.logs.set(sessionId, log);
|
|
181
204
|
}
|
|
182
205
|
return log;
|
|
183
206
|
}
|
|
184
207
|
|
|
208
|
+
memoryStats(): EventLogMemoryStats {
|
|
209
|
+
let eventsRetained = 0;
|
|
210
|
+
let eventsTotal = 0;
|
|
211
|
+
for (const log of this.opened.values()) {
|
|
212
|
+
const stats = log.memoryStats();
|
|
213
|
+
eventsRetained += stats.eventsRetained;
|
|
214
|
+
eventsTotal += stats.eventsTotal;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
residentSessions: this.logs.size,
|
|
218
|
+
eventsRetained,
|
|
219
|
+
eventsTotal,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
185
223
|
async flushAll(): Promise<void> {
|
|
186
224
|
await Promise.all(
|
|
187
225
|
[...this.logs.values()].map((logPromise) =>
|
|
@@ -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
|
}
|