@cursor/july 0.1.87 → 0.1.89
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 -1
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +4 -2
- package/dist/internal/cli-ax.js +1 -1
- package/dist/internal/cli-deploy.d.ts +3 -2
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +84 -9
- package/dist/internal/cli-docs.js +11 -0
- package/dist/internal/cloud-merge.d.ts +3 -1
- package/dist/internal/cloud-merge.d.ts.map +1 -1
- package/dist/internal/cloud-merge.js +10 -2
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +8 -4
- package/dist/internal/cursor/backend-client.d.ts +12 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +27 -1
- package/dist/internal/deploy-client.d.ts +42 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +70 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +87 -11
- package/dist/internal/docs-site.d.ts +13 -2
- package/dist/internal/docs-site.d.ts.map +1 -1
- package/dist/internal/docs-site.js +76 -13
- package/dist/internal/grokbot/runner.d.ts +61 -0
- package/dist/internal/grokbot/runner.d.ts.map +1 -0
- package/dist/internal/grokbot/runner.js +278 -0
- package/dist/internal/mcp-endpoint.js +6 -3
- package/dist/internal/mcp-host.d.ts +14 -1
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +41 -2
- package/dist/internal/runtime-dispatch-runner.d.ts +30 -0
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -0
- package/dist/internal/runtime-dispatch-runner.js +60 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +7 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +24 -1
- package/dist/internal/session-engine.d.ts +9 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +121 -46
- package/dist/internal/tool-policy.d.ts +33 -1
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +53 -0
- package/dist/playground/assets/{index-Bqn91tW4.js → index-BmiIjFlM.js} +45 -45
- package/dist/playground/assets/index-DQGZnAI0.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +86 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/skills/debug/SKILL.md +1 -2
- package/skills/framework-map/SKILL.md +1 -2
- package/src/bin/agent-serve.ts +9 -1
- package/src/internal/advertise-tools.ts +7 -0
- package/src/internal/cli-ax.ts +1 -1
- package/src/internal/cli-deploy.ts +123 -15
- package/src/internal/cli-docs.ts +11 -0
- package/src/internal/cloud-merge.ts +12 -2
- package/src/internal/cursor/account-mcp.ts +15 -1
- package/src/internal/cursor/backend-client.ts +37 -0
- package/src/internal/deploy-client.ts +122 -2
- package/src/internal/discovery.ts +133 -13
- package/src/internal/docs-site.ts +83 -13
- package/src/internal/grokbot/runner.ts +361 -0
- package/src/internal/mcp-endpoint.ts +6 -1
- package/src/internal/mcp-host.ts +48 -0
- package/src/internal/runtime-dispatch-runner.ts +63 -0
- package/src/internal/sdk-runner.ts +9 -0
- package/src/internal/server.ts +32 -4
- package/src/internal/session-engine.ts +126 -21
- package/src/internal/tool-policy.ts +80 -1
- package/src/types.ts +84 -2
- package/dist/playground/assets/index-C0_5hOsf.css +0 -1
|
@@ -436,7 +436,7 @@ async function loadAgent(
|
|
|
436
436
|
);
|
|
437
437
|
const seedFiles = await loadSeedFiles(ctx, agentDir);
|
|
438
438
|
const subagents = await loadSubagents(ctx, join(agentDir, "subagents"));
|
|
439
|
-
const runtime = config?.runtime
|
|
439
|
+
const runtime = resolveRuntime(ctx, agentDir, config?.runtime);
|
|
440
440
|
// Keep authored `cloud` even when default runtime is local — per-session
|
|
441
441
|
// `send({ cloud })` merges it under cloudOverride for handoff sessions.
|
|
442
442
|
const cloud = config?.cloud;
|
|
@@ -460,29 +460,30 @@ async function loadAgent(
|
|
|
460
460
|
authoredAllowlist === undefined || authoredAllowlist.includes("mcp")
|
|
461
461
|
? authoredAllowlist
|
|
462
462
|
: [...authoredAllowlist, "mcp" as const];
|
|
463
|
-
// The allowlist states runtime-agnostic intent, but the
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
|
|
463
|
+
// The allowlist states runtime-agnostic intent, but only the local
|
|
464
|
+
// harness can enforce it (the Cursor SDK rejects tool restrictions on
|
|
465
|
+
// cloud agents, and the Grok Bot harness has its own fixed toolset) — a
|
|
466
|
+
// non-local agent that declares one must fail at serve start, never run
|
|
467
|
+
// unrestricted. (Hybrid per-send cloud sessions fail closed in the
|
|
468
|
+
// session engine.)
|
|
469
|
+
if (allowedTools !== undefined && runtime !== "local") {
|
|
468
470
|
ctx.diagnostics.push({
|
|
469
471
|
severity: "error",
|
|
470
472
|
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
471
|
-
message:
|
|
472
|
-
'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.',
|
|
473
|
+
message: `tools cannot be enforced on runtime: "${runtime}". Remove \`tools\` or run the agent on the local runtime.`,
|
|
473
474
|
});
|
|
474
475
|
}
|
|
475
476
|
// Per-session connection features (defineConnection auth/advertiseTools)
|
|
476
477
|
// only run on local turns: advertised tools are in-process server tools,
|
|
477
478
|
// and per-session headers cannot reach a cloud VM's MCP config. A
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
479
|
+
// non-local agent that declares them must fail at serve start — never
|
|
480
|
+
// run without the connection's tools or identity. (Hybrid per-send
|
|
481
|
+
// cloud sessions fail closed in the session engine.)
|
|
481
482
|
const perSessionConnection = connections.find(
|
|
482
483
|
(connection) =>
|
|
483
484
|
connection.advertiseTools === true || connection.auth !== undefined
|
|
484
485
|
);
|
|
485
|
-
if (perSessionConnection !== undefined && runtime
|
|
486
|
+
if (perSessionConnection !== undefined && runtime !== "local") {
|
|
486
487
|
ctx.diagnostics.push({
|
|
487
488
|
severity: "error",
|
|
488
489
|
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
@@ -532,9 +533,22 @@ async function loadAgent(
|
|
|
532
533
|
hasCloudOptions: cloud !== undefined,
|
|
533
534
|
});
|
|
534
535
|
}
|
|
535
|
-
|
|
536
536
|
appendBuiltinTools(ctx, agentDir, tools, config?.builtinTools);
|
|
537
537
|
|
|
538
|
+
// After appendBuiltinTools so opted-in built-ins (server-executed) count.
|
|
539
|
+
if (runtime === "grokbot") {
|
|
540
|
+
checkGrokBotRuntimeLimitations(ctx, agentDir, {
|
|
541
|
+
serverTools: tools.filter((tool) => tool.execution === "server").length,
|
|
542
|
+
agentTools: tools.filter((tool) => tool.execution === "agent").length,
|
|
543
|
+
skills: skills.length,
|
|
544
|
+
seedFiles: seedFiles.length,
|
|
545
|
+
connections: connections.length,
|
|
546
|
+
subagents: subagents.length,
|
|
547
|
+
hasCloudOptions: cloud !== undefined,
|
|
548
|
+
hasLocalSandbox: localSandbox,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
538
552
|
return {
|
|
539
553
|
name,
|
|
540
554
|
description: config?.description,
|
|
@@ -1575,6 +1589,112 @@ function appendBuiltinTools(
|
|
|
1575
1589
|
}
|
|
1576
1590
|
}
|
|
1577
1591
|
|
|
1592
|
+
/**
|
|
1593
|
+
* Validate the authored runtime. An unrecognized value is an error
|
|
1594
|
+
* diagnostic and falls back to local — a typo must not silently pick a
|
|
1595
|
+
* different harness for every turn.
|
|
1596
|
+
*/
|
|
1597
|
+
function resolveRuntime(
|
|
1598
|
+
ctx: DiscoveryContext,
|
|
1599
|
+
agentDir: string,
|
|
1600
|
+
runtime: AgentRuntime | undefined
|
|
1601
|
+
): AgentRuntime {
|
|
1602
|
+
if (
|
|
1603
|
+
runtime === undefined ||
|
|
1604
|
+
runtime === "local" ||
|
|
1605
|
+
runtime === "cloud" ||
|
|
1606
|
+
runtime === "grokbot"
|
|
1607
|
+
) {
|
|
1608
|
+
return runtime ?? "local";
|
|
1609
|
+
}
|
|
1610
|
+
ctx.diagnostics.push({
|
|
1611
|
+
severity: "error",
|
|
1612
|
+
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
1613
|
+
message: `runtime must be "local", "cloud", or "grokbot"; got ${JSON.stringify(runtime)}.`,
|
|
1614
|
+
});
|
|
1615
|
+
return "local";
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
/**
|
|
1619
|
+
* Grok Bot turns run on the Sand harness with its own toolset: authored
|
|
1620
|
+
* server tools, MCP connections, and subagents have no path onto that
|
|
1621
|
+
* toolset yet, so an agent declaring them must fail at serve start rather
|
|
1622
|
+
* than run with capabilities silently missing.
|
|
1623
|
+
*/
|
|
1624
|
+
function checkGrokBotRuntimeLimitations(
|
|
1625
|
+
ctx: DiscoveryContext,
|
|
1626
|
+
agentDir: string,
|
|
1627
|
+
counts: {
|
|
1628
|
+
serverTools: number;
|
|
1629
|
+
agentTools: number;
|
|
1630
|
+
skills: number;
|
|
1631
|
+
seedFiles: number;
|
|
1632
|
+
connections: number;
|
|
1633
|
+
subagents: number;
|
|
1634
|
+
hasCloudOptions: boolean;
|
|
1635
|
+
hasLocalSandbox: boolean;
|
|
1636
|
+
}
|
|
1637
|
+
): void {
|
|
1638
|
+
const agentPath = displayPath(ctx, join(agentDir, "agent.ts"));
|
|
1639
|
+
const error = (path: string, message: string): void => {
|
|
1640
|
+
ctx.diagnostics.push({ severity: "error", path, message });
|
|
1641
|
+
};
|
|
1642
|
+
const warn = (path: string, message: string): void => {
|
|
1643
|
+
ctx.diagnostics.push({ severity: "warning", path, message });
|
|
1644
|
+
};
|
|
1645
|
+
if (counts.serverTools > 0) {
|
|
1646
|
+
error(
|
|
1647
|
+
displayPath(ctx, join(agentDir, "tools")),
|
|
1648
|
+
'Server tools (execution: "server", including opted-in builtinTools) are not reachable from grokbot turns yet. Remove them or run the agent on the local runtime.'
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
if (counts.connections > 0) {
|
|
1652
|
+
error(
|
|
1653
|
+
displayPath(ctx, join(agentDir, "mcp-connections")),
|
|
1654
|
+
"MCP connections are not wired into grokbot turns yet. Remove them or run the agent on the local runtime."
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
if (counts.subagents > 0) {
|
|
1658
|
+
error(
|
|
1659
|
+
displayPath(ctx, join(agentDir, "subagents")),
|
|
1660
|
+
"Authored subagents are not available on grokbot turns (the Grok Bot harness runs its own subagent set). Remove them or run the agent on the local runtime."
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
// The remaining declarations mirror the cloud runtime's warnings: the
|
|
1664
|
+
// turn runs on a hosted machine (the account's box), so local-workspace
|
|
1665
|
+
// materialization never reaches it.
|
|
1666
|
+
if (counts.agentTools > 0) {
|
|
1667
|
+
warn(
|
|
1668
|
+
displayPath(ctx, join(agentDir, "tools")),
|
|
1669
|
+
'Agent tools (execution: "agent") are described in the hosted session\'s instructions (with script bodies); they are not written onto the box filesystem automatically.'
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
if (counts.skills > 0) {
|
|
1673
|
+
warn(
|
|
1674
|
+
displayPath(ctx, join(agentDir, "skills")),
|
|
1675
|
+
"Authored skills are materialized into the local session workspace only; the hosted Grok Bot box does not see them."
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
if (counts.seedFiles > 0) {
|
|
1679
|
+
warn(
|
|
1680
|
+
displayPath(ctx, join(agentDir, "sandbox")),
|
|
1681
|
+
"sandbox/workspace seed files apply to the local session workspace only; grokbot turns run on the account's hosted box."
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
if (counts.hasCloudOptions) {
|
|
1685
|
+
warn(
|
|
1686
|
+
agentPath,
|
|
1687
|
+
'The `cloud` block is unused on runtime: "grokbot", and grokbot sessions refuse per-session cloud attach.'
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
if (counts.hasLocalSandbox) {
|
|
1691
|
+
warn(
|
|
1692
|
+
agentPath,
|
|
1693
|
+
'local.sandbox does not apply to runtime: "grokbot"; the box is the account\'s Cursor-hosted computer.'
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1578
1698
|
function warnCloudRuntimeLimitations(
|
|
1579
1699
|
ctx: DiscoveryContext,
|
|
1580
1700
|
agentDir: string,
|
|
@@ -6,10 +6,16 @@
|
|
|
6
6
|
* resolve under the mount). `serve` auto-builds it when the local
|
|
7
7
|
* vitepress toolchain is present (source checkouts); published installs
|
|
8
8
|
* ship the prebuilt site inside `dist/**`.
|
|
9
|
+
*
|
|
10
|
+
* Set `AGENT_SERVE_DOCS_DIST` to an overlay directory that contains a built
|
|
11
|
+
* site (`index.html`). When that path exists, `/docs` is served from it so a
|
|
12
|
+
* long-lived host (corp ECS) can pick up the published `@cursor/july` docs
|
|
13
|
+
* without rebuilding the process image. Requests re-read the path, so an
|
|
14
|
+
* atomic symlink swap is visible without a restart.
|
|
9
15
|
*/
|
|
10
16
|
|
|
11
17
|
import { spawn } from "node:child_process";
|
|
12
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
18
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
13
19
|
import { createRequire } from "node:module";
|
|
14
20
|
import { dirname, extname, join, normalize, sep } from "node:path";
|
|
15
21
|
import { fileURLToPath } from "node:url";
|
|
@@ -21,14 +27,69 @@ const DOCS_DIST_DIR = fileURLToPath(
|
|
|
21
27
|
new URL("../../dist/docs/", import.meta.url)
|
|
22
28
|
);
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
const DOCS_OVERLAY_ENV = "AGENT_SERVE_DOCS_DIST";
|
|
31
|
+
|
|
32
|
+
function realDir(dir: string): string | undefined {
|
|
33
|
+
try {
|
|
34
|
+
return realpathSync(dir);
|
|
35
|
+
} catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isInsideRoot(fileReal: string, rootReal: string): boolean {
|
|
41
|
+
const prefix = rootReal.endsWith(sep) ? rootReal : `${rootReal}${sep}`;
|
|
42
|
+
return fileReal === rootReal || fileReal.startsWith(prefix);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve `candidate` to a regular file whose real path stays under
|
|
47
|
+
* `rootReal`. Follows overlay / site-root symlinks, but refuses a planted
|
|
48
|
+
* symlink (or hard-linked path) that escapes the docs tree.
|
|
49
|
+
*/
|
|
50
|
+
function containedFile(
|
|
51
|
+
candidate: string,
|
|
52
|
+
rootReal: string
|
|
53
|
+
): string | undefined {
|
|
54
|
+
try {
|
|
55
|
+
const real = realpathSync(candidate);
|
|
56
|
+
if (!isInsideRoot(real, rootReal) || !statSync(real).isFile()) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return real;
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function hasDocsIndex(distDir: string): boolean {
|
|
66
|
+
const root = realDir(distDir);
|
|
67
|
+
if (root === undefined) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return containedFile(join(distDir, "index.html"), root) !== undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Absolute path of the VitePress site shipped in this install (ignores overlay). */
|
|
74
|
+
export function shippedDocsDistDir(): string {
|
|
75
|
+
return DOCS_DIST_DIR;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Directory `/docs` is served from. Prefers `AGENT_SERVE_DOCS_DIST` when that
|
|
80
|
+
* overlay has a built site; otherwise the install's `dist/docs/`.
|
|
81
|
+
*/
|
|
25
82
|
export function docsDistDir(): string {
|
|
83
|
+
const overlay = process.env[DOCS_OVERLAY_ENV]?.trim();
|
|
84
|
+
if (overlay !== undefined && overlay !== "" && hasDocsIndex(overlay)) {
|
|
85
|
+
return overlay;
|
|
86
|
+
}
|
|
26
87
|
return DOCS_DIST_DIR;
|
|
27
88
|
}
|
|
28
89
|
|
|
29
|
-
/** Whether
|
|
30
|
-
export function docsSiteExists(distDir: string =
|
|
31
|
-
return
|
|
90
|
+
/** Whether a built docs site is available at `distDir` (overlay or shipped). */
|
|
91
|
+
export function docsSiteExists(distDir: string = docsDistDir()): boolean {
|
|
92
|
+
return hasDocsIndex(distDir);
|
|
32
93
|
}
|
|
33
94
|
|
|
34
95
|
/**
|
|
@@ -39,7 +100,7 @@ export function docsSiteExists(distDir: string = DOCS_DIST_DIR): boolean {
|
|
|
39
100
|
*/
|
|
40
101
|
export function resolveDocsFile(
|
|
41
102
|
requestPath: string,
|
|
42
|
-
distDir: string =
|
|
103
|
+
distDir: string = docsDistDir()
|
|
43
104
|
): string | undefined {
|
|
44
105
|
const decoded = requestPath
|
|
45
106
|
.split("/")
|
|
@@ -49,14 +110,19 @@ export function resolveDocsFile(
|
|
|
49
110
|
if (normalized.startsWith("..") || normalized.includes(`..${sep}`)) {
|
|
50
111
|
return undefined;
|
|
51
112
|
}
|
|
113
|
+
const rootReal = realDir(distDir);
|
|
114
|
+
if (rootReal === undefined) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
52
117
|
const root = distDir.endsWith(sep) ? distDir : `${distDir}${sep}`;
|
|
53
118
|
const base = join(root, normalized);
|
|
54
119
|
if (base !== root.slice(0, -1) && !base.startsWith(root)) {
|
|
55
120
|
return undefined;
|
|
56
121
|
}
|
|
57
122
|
for (const candidate of [base, join(base, "index.html"), `${base}.html`]) {
|
|
58
|
-
|
|
59
|
-
|
|
123
|
+
const file = containedFile(candidate, rootReal);
|
|
124
|
+
if (file !== undefined) {
|
|
125
|
+
return file;
|
|
60
126
|
}
|
|
61
127
|
}
|
|
62
128
|
return undefined;
|
|
@@ -65,7 +131,7 @@ export function resolveDocsFile(
|
|
|
65
131
|
/** Serve one path from the built docs site. */
|
|
66
132
|
export function renderDocsFile(
|
|
67
133
|
requestPath: string,
|
|
68
|
-
distDir: string =
|
|
134
|
+
distDir: string = docsDistDir()
|
|
69
135
|
): Response {
|
|
70
136
|
if (!docsSiteExists(distDir)) {
|
|
71
137
|
return new Response(fallbackHtml(), {
|
|
@@ -75,9 +141,13 @@ export function renderDocsFile(
|
|
|
75
141
|
}
|
|
76
142
|
const filePath = resolveDocsFile(requestPath, distDir);
|
|
77
143
|
if (filePath === undefined) {
|
|
78
|
-
const
|
|
144
|
+
const root = realDir(distDir);
|
|
145
|
+
const notFound =
|
|
146
|
+
root === undefined
|
|
147
|
+
? undefined
|
|
148
|
+
: containedFile(join(distDir, "404.html"), root);
|
|
79
149
|
return new Response(
|
|
80
|
-
|
|
150
|
+
notFound !== undefined ? readFileSync(notFound, "utf8") : "Not found",
|
|
81
151
|
{
|
|
82
152
|
status: 404,
|
|
83
153
|
headers: { "content-type": "text/html; charset=utf-8" },
|
|
@@ -110,7 +180,7 @@ export function renderDocsFile(
|
|
|
110
180
|
export async function ensureDocsSiteBundle(
|
|
111
181
|
logger: (line: string) => void = console.log
|
|
112
182
|
): Promise<void> {
|
|
113
|
-
if (docsSiteExists()) {
|
|
183
|
+
if (docsSiteExists(DOCS_DIST_DIR)) {
|
|
114
184
|
return;
|
|
115
185
|
}
|
|
116
186
|
const toolchain = resolveDocsToolchain();
|
|
@@ -133,7 +203,7 @@ export async function ensureDocsSiteBundle(
|
|
|
133
203
|
if (code !== 0) {
|
|
134
204
|
throw new Error(`docs vitepress build failed (exit ${code})`);
|
|
135
205
|
}
|
|
136
|
-
if (!docsSiteExists()) {
|
|
206
|
+
if (!docsSiteExists(DOCS_DIST_DIR)) {
|
|
137
207
|
throw new Error(
|
|
138
208
|
"docs vitepress build finished but dist/docs/index.html is missing"
|
|
139
209
|
);
|