mirror of
https://github.com/vercel/eve.git
synced 2026-09-20 05:35:39 +08:00
feat(eve): discover Next.js workspace agents (#3343)
Signed-off-by: owenkephart <owen.kephart@vercel.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"eve": patch
|
||||
---
|
||||
|
||||
Make `withEve()` discover project-level `agents/` workspace members, mounting each named agent through a Next.js app without repeating the agent map in `next.config.ts`. Workspace peers declared with `defineWorkspaceAgent()` route through the named Next.js mount automatically.
|
||||
@@ -3,7 +3,7 @@
|
||||
These apps verify eve's frontend framework integrations and act as runnable examples for maintainers.
|
||||
|
||||
- `framework-next` covers `eve/next` and `withEve()`.
|
||||
- `framework-next-multi-agent` covers `withEve({ agents })` and named `useEveAgent({ agent })` calls.
|
||||
- `framework-next-multi-agent` covers `withEve()` workspace discovery and named `useEveAgent({ agent })` calls.
|
||||
- `framework-nuxt` covers the `eve/nuxt` module.
|
||||
- `framework-sveltekit` covers the `eve/sveltekit` Vite plugin.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Next.js multi-agent eve demo
|
||||
|
||||
This app demonstrates `withEve({ agents })` with three independent eve agents
|
||||
mounted into one Next.js app:
|
||||
This app demonstrates `withEve()` discovering three independent eve agents
|
||||
from the project-level `agents/` workspace and mounting them into one Next.js app:
|
||||
|
||||
- `support` at `/eve/agents/support/eve/v1/*`
|
||||
- `billing` at `/eve/agents/billing/eve/v1/*`
|
||||
|
||||
@@ -3,14 +3,4 @@ import { withEve } from "eve/next";
|
||||
|
||||
const nextConfig: NextConfig = {};
|
||||
|
||||
export default withEve(nextConfig, {
|
||||
agents: {
|
||||
support: "./agents/support",
|
||||
billing: {
|
||||
root: "./agents/billing",
|
||||
buildCommand: "pnpm --dir ../.. build:billing-agent",
|
||||
servicePrefix: "/_eve_internal/billing",
|
||||
},
|
||||
research: "./agents/research",
|
||||
},
|
||||
});
|
||||
export default withEve(nextConfig);
|
||||
|
||||
@@ -22,7 +22,9 @@ const nextConfig: NextConfig = {};
|
||||
export default withEve(nextConfig);
|
||||
```
|
||||
|
||||
By default `withEve()` looks for an `agent/` folder inside your Next.js project root. If the agent lives somewhere else, point at it with `eveRoot`:
|
||||
By default `withEve()` looks for an `agent/` folder inside your Next.js project root. When the project root is an eve workspace with `agents/<name>/` members, it discovers every member and mounts each at `/eve/agents/<name>/eve/v1/*` instead.
|
||||
|
||||
If one agent lives somewhere else, point at it with `eveRoot`:
|
||||
|
||||
```ts
|
||||
export default withEve(nextConfig, {
|
||||
@@ -30,7 +32,7 @@ export default withEve(nextConfig, {
|
||||
});
|
||||
```
|
||||
|
||||
For multiple agents, use `agents`. String values are agent roots; object values can override the build command or private production service prefix for that agent:
|
||||
To mount agents that are not members of the project-level `agents/` workspace, use `agents`. String values are agent roots; object values can override the build command or private production service prefix for that agent:
|
||||
|
||||
```ts
|
||||
export default withEve(nextConfig, {
|
||||
@@ -60,13 +62,13 @@ Generated agent services build with `EVE_PUBLIC_ROUTE_PREFIX` set to the agent's
|
||||
|
||||
All fields are optional.
|
||||
|
||||
| Option | Type | Default | Purpose |
|
||||
| -------------------- | --------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `eveRoot` | `string` | Next.js app root | Path to one unnamed eve app root, relative to `process.cwd()` unless absolute. Do not combine with `agents`. |
|
||||
| `agents` | `Record<string, ...>` | unset | Named eve agents to mount under `/eve/agents/<name>/eve/v1/*`. Each value is a root string or `{ root, buildCommand?, servicePrefix? }`. |
|
||||
| `eveBuildCommand` | `string` | generated | Build command for generated eve Vercel services. In multi-agent mode this is the default for agents without their own `buildCommand`. |
|
||||
| `servicePrefix` | `string` | `"/_eve_internal/eve"` | Private route namespace for legacy manual Vercel service configs and non-Vercel production proxying. Named agents derive unique defaults from this prefix. |
|
||||
| `devServerTimeoutMs` | `number` | `180000` | Maximum time to wait for each eve development server to become available. |
|
||||
| Option | Type | Default | Purpose |
|
||||
| -------------------- | --------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `eveRoot` | `string` | Next.js app root | Path to one unnamed eve app root, relative to `process.cwd()` unless absolute. Do not combine with `agents`. |
|
||||
| `agents` | `Record<string, ...>` | inferred for a workspace | Named eve agents to mount under `/eve/agents/<name>/eve/v1/*`. `withEve()` discovers project-level `agents/<name>/` members when neither `agents` nor `eveRoot` is set; otherwise each value is a root string or `{ root, buildCommand?, servicePrefix? }`. |
|
||||
| `eveBuildCommand` | `string` | generated | Build command for generated eve Vercel services. In multi-agent mode this is the default for agents without their own `buildCommand`. |
|
||||
| `servicePrefix` | `string` | `"/_eve_internal/eve"` | Private route namespace for legacy manual Vercel service configs and non-Vercel production proxying. Named agents derive unique defaults from this prefix. |
|
||||
| `devServerTimeoutMs` | `number` | `180000` | Maximum time to wait for each eve development server to become available. |
|
||||
|
||||
For slow cold starts, increase the development timeout:
|
||||
|
||||
@@ -124,7 +126,7 @@ See [Resumable sessions](./overview#resumable-sessions) for persistence and repl
|
||||
});
|
||||
```
|
||||
|
||||
- **Local production build.** `next build && next start` serves the eve runtime from its built `.output/server/index.mjs` on a stable local port (`4274`) and proxies the eve routes to it. Run `eve build` first so that output exists. Change the port with `EVE_NEXT_PRODUCTION_PORT`:
|
||||
- **Local production build.** `next build && next start` serves the eve runtime from its built `.output/server/index.mjs` on a stable local port (`4274`) and proxies the eve routes to it. Run `eve build` first so that output exists. In an `agents/` workspace, build every member from its `agents/<name>/` directory before starting Next.js. Change the port with `EVE_NEXT_PRODUCTION_PORT`:
|
||||
|
||||
```bash
|
||||
EVE_NEXT_PRODUCTION_PORT=5000 npm run build && npm start
|
||||
|
||||
@@ -19,7 +19,7 @@ eve add memory/file
|
||||
|
||||
If an item is not found, `eve add` searches the available catalogs and prints close matches without installing anything.
|
||||
|
||||
Web Chat installs a project-level Next.js application and cannot currently be added to a top-level `agents/` workspace. For that topology, create a root Next.js application and configure [`withEve({ agents })`](./guides/frontend/nextjs) instead. eve rejects `eve add channel/web` before writing files when the selected agent belongs to such a workspace.
|
||||
Web Chat installs a project-level Next.js application and cannot currently be added to a top-level `agents/` workspace. For that topology, create a root Next.js application and wrap its config with [`withEve()`](./guides/frontend/nextjs); eve discovers the workspace agents automatically. eve rejects `eve add channel/web` before writing files when the selected agent belongs to such a workspace.
|
||||
|
||||
If you do not know an item name yet, run `eve add` without an argument. Its help output shows how to search the registry with `eve registry search <query>`.
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ export default defineWorkspaceAgent({
|
||||
});
|
||||
```
|
||||
|
||||
The helper uses the peer's root `defineAgent({ description })` value as the model-visible tool description. Pass `description` to override it for this caller. When `transport` is omitted, eve selects the built-in transport for the runtime environment. Vercel deployments route through the current deployment and authenticate with the caller's Vercel OIDC token. Outside Vercel, provide an explicit transport with `url` and optional `auth` or `headers`:
|
||||
The helper uses the peer's root `defineAgent({ description })` value as the model-visible tool description. Pass `description` to override it for this caller. When `transport` is omitted, eve selects the built-in transport for the runtime environment. Vercel deployments route through the current deployment and authenticate with the caller's Vercel OIDC token. A workspace hosted through `withEve()` in a Next.js app uses its `/eve/agents/<name>` mount; a hostless workspace uses `/<name>`. Outside Vercel, provide an explicit transport with `url` and optional `auth` or `headers`:
|
||||
|
||||
```ts title="agents/foreman/agent/subagents/research.ts"
|
||||
import { defineWorkspaceAgent } from "eve";
|
||||
|
||||
@@ -27,6 +27,19 @@ describe("defineWorkspaceAgent", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the Next.js named-agent route when the caller is mounted through eve/next", async () => {
|
||||
vi.stubEnv("EVE_PUBLIC_ROUTE_PREFIX", "/eve/agents/support");
|
||||
vi.stubEnv("VERCEL", "1");
|
||||
vi.stubEnv("VERCEL_ENV", "preview");
|
||||
vi.stubEnv("VERCEL_URL", "preview.example.com");
|
||||
|
||||
const subagent = defineWorkspaceAgent({ name: "research" });
|
||||
|
||||
expect((subagent.url as () => string)()).toBe(
|
||||
"https://preview.example.com/eve/agents/research",
|
||||
);
|
||||
});
|
||||
|
||||
it("requires an explicit transport outside Vercel", async () => {
|
||||
vi.stubEnv("VERCEL", undefined);
|
||||
const subagent = defineWorkspaceAgent({ name: "research" });
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type RemoteAgentUrl,
|
||||
} from "#public/definitions/remote-agent.js";
|
||||
import type { JsonObject } from "#shared/json.js";
|
||||
import { normalizePublicRoutePrefix } from "#shared/public-route-prefix.js";
|
||||
|
||||
const WORKSPACE_AGENT_NAME = Symbol.for("eve.workspace-agent.name");
|
||||
|
||||
@@ -75,7 +76,10 @@ function defaultWorkspaceAgentTransport(name: string): WorkspaceAgentTransport {
|
||||
"The default workspace-agent transport requires VERCEL_URL, or VERCEL_PROJECT_PRODUCTION_URL in production.",
|
||||
);
|
||||
}
|
||||
return `https://${host}/${name}`;
|
||||
const callerRoutePrefix = normalizePublicRoutePrefix(process.env.EVE_PUBLIC_ROUTE_PREFIX);
|
||||
const peerRoutePrefix =
|
||||
callerRoutePrefix?.startsWith("/eve/agents/") === true ? `/eve/agents/${name}` : `/${name}`;
|
||||
return `https://${host}${peerRoutePrefix}`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,21 +44,19 @@ describe("withEve Vercel config", () => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("does not create Build Output config outside Vercel when no Vercel project is detected", async () => {
|
||||
it("requires a local eve build outside Vercel", async () => {
|
||||
const appRoot = await createTempAppRoot();
|
||||
process.chdir(appRoot);
|
||||
vi.stubEnv("NODE_ENV", "production");
|
||||
|
||||
const config = await resolveConfig(withEve<TestConfig>({}));
|
||||
const rewrites = await config.rewrites?.();
|
||||
|
||||
await expect(config.rewrites?.()).rejects.toThrow(
|
||||
`Run eve build from ${appRoot} before starting Next.js.`,
|
||||
);
|
||||
await expect(
|
||||
readFile(join(appRoot, ".vercel", "output", "config.json"), "utf8"),
|
||||
).rejects.toThrow();
|
||||
expect(getBeforeFiles(rewrites)).toContainEqual({
|
||||
destination: "http://127.0.0.1:4274/eve/v1/:path+",
|
||||
source: "/eve/v1/:path+",
|
||||
});
|
||||
});
|
||||
|
||||
it("writes Build Output config in Vercel even when no linked project is detected", async () => {
|
||||
@@ -291,6 +289,55 @@ describe("withEve Vercel config", () => {
|
||||
expect(rewrites).toBeUndefined();
|
||||
});
|
||||
|
||||
it("discovers workspace agents when the Next.js app owns the workspace", async () => {
|
||||
const appRoot = await createTempAppRoot();
|
||||
process.chdir(appRoot);
|
||||
await Promise.all([
|
||||
mkdir(join(appRoot, "agents", "support", "agent"), { recursive: true }),
|
||||
mkdir(join(appRoot, "agents", "research", "agent"), { recursive: true }),
|
||||
writeFile(join(appRoot, "package.json"), JSON.stringify({ dependencies: { eve: "*" } })),
|
||||
]);
|
||||
vi.stubEnv("NODE_ENV", "production");
|
||||
vi.stubEnv("VERCEL", "1");
|
||||
vi.stubEnv("VERCEL_URL", "preview.example.com");
|
||||
|
||||
const config = await resolveConfig(withEve<TestConfig>({}));
|
||||
const rewrites = await config.rewrites?.();
|
||||
const outputConfig = await readJsonFile(join(appRoot, ".vercel", "output", "config.json"));
|
||||
|
||||
expect(outputConfig).toMatchObject({
|
||||
routes: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
destination: { service: "eve-research", type: "service" },
|
||||
src: "^/eve/agents/research/eve/v1/(.*)$",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
destination: { service: "eve-support", type: "service" },
|
||||
src: "^/eve/agents/support/eve/v1/(.*)$",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
destination: { service: "eve-research", type: "service" },
|
||||
src: "^/eve/agents/research/?$",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
destination: { service: "eve-support", type: "service" },
|
||||
src: "^/eve/agents/support/?$",
|
||||
}),
|
||||
]),
|
||||
services: expect.objectContaining({
|
||||
"eve-research": expect.objectContaining({
|
||||
buildCommand: expect.stringContaining("EVE_INTERNAL_AGENT_WORKSPACE_MEMBER=1"),
|
||||
routePrefix: "/eve/agents/research",
|
||||
}),
|
||||
"eve-support": expect.objectContaining({
|
||||
buildCommand: expect.stringContaining("EVE_INTERNAL_AGENT_WORKSPACE_MEMBER=1"),
|
||||
routePrefix: "/eve/agents/support",
|
||||
}),
|
||||
}),
|
||||
});
|
||||
expect(rewrites).toBeUndefined();
|
||||
});
|
||||
|
||||
it("accepts a custom eve service build command", async () => {
|
||||
const appRoot = await createTempAppRoot();
|
||||
process.chdir(appRoot);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { isAbsolute, resolve } from "node:path";
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
import { assertValidPublicAgentName } from "#internal/agent-name.js";
|
||||
import { findEveProjectContext } from "#internal/project-context.js";
|
||||
import { quoteVercelShellArgument, toVercelRelativePath } from "#internal/vercel/build-command.js";
|
||||
import { EVE_ROUTE_PREFIX } from "#protocol/routes.js";
|
||||
import { resolveEveBinaryPath } from "#shared/resolve-eve-binary.js";
|
||||
@@ -110,15 +111,17 @@ export interface WithEveOptions {
|
||||
readonly devServerTimeoutMs?: number;
|
||||
/**
|
||||
* Path to the eve application root, relative to `process.cwd()` unless
|
||||
* absolute. Defaults to the Next.js app root.
|
||||
* absolute. Defaults to the Next.js app root when it is not an eve workspace.
|
||||
*/
|
||||
readonly eveRoot?: string;
|
||||
/**
|
||||
* Named eve agents to mount under `/eve/agents/<name>/eve/v1/*`.
|
||||
*
|
||||
* Use this when one Next.js app needs to talk to multiple eve agents. When
|
||||
* set, do not also set {@link eveRoot}; the single-agent form remains the
|
||||
* shorthand for one unnamed agent mounted at `/eve/v1/*`.
|
||||
* Use this when one Next.js app needs to talk to multiple eve agents outside
|
||||
* a project-level `agents/` workspace. When unset, withEve discovers that
|
||||
* workspace's members automatically. Do not combine with {@link eveRoot};
|
||||
* the single-agent form remains the shorthand for one unnamed agent mounted
|
||||
* at `/eve/v1/*`.
|
||||
*/
|
||||
readonly agents?: WithEveAgentsConfig;
|
||||
/**
|
||||
@@ -146,6 +149,7 @@ interface ResolvedEveNextAgent {
|
||||
readonly name?: string;
|
||||
readonly publicRoutePrefix: string;
|
||||
readonly servicePrefix: string;
|
||||
readonly workspaceMember?: boolean;
|
||||
}
|
||||
|
||||
function resolveApplicationRoot(appPath: string | undefined): string {
|
||||
@@ -313,6 +317,18 @@ function assertValidAgentName(name: string): void {
|
||||
assertValidPublicAgentName(name, "eve Next.js agent name");
|
||||
}
|
||||
|
||||
function assertValidWithEveOptions(options: WithEveOptions): void {
|
||||
if (options.agents === undefined) return;
|
||||
if (options.eveRoot !== undefined) {
|
||||
throw new Error("withEve cannot combine eveRoot with agents. Use one configuration form.");
|
||||
}
|
||||
const agentNames = Object.keys(options.agents);
|
||||
if (agentNames.length === 0) {
|
||||
throw new Error("withEve agents must contain at least one named eve agent.");
|
||||
}
|
||||
for (const name of agentNames) assertValidAgentName(name);
|
||||
}
|
||||
|
||||
function createDefaultBuildCommand(input: { readonly agentRoot: string }): string {
|
||||
const eveBinaryPath = toVercelRelativePath(
|
||||
input.agentRoot,
|
||||
@@ -321,12 +337,34 @@ function createDefaultBuildCommand(input: { readonly agentRoot: string }): strin
|
||||
return `node ${quoteVercelShellArgument(eveBinaryPath)} build`;
|
||||
}
|
||||
|
||||
function normalizeAgentsConfig(options: WithEveOptions): readonly ResolvedEveNextAgent[] {
|
||||
async function normalizeAgentsConfig(
|
||||
options: WithEveOptions,
|
||||
nextRoot: string,
|
||||
): Promise<readonly ResolvedEveNextAgent[]> {
|
||||
const servicePrefixBase = normalizeRoutePrefix(options.servicePrefix ?? EVE_NEXT_SERVICE_PREFIX);
|
||||
const resolveBuildCommand = (agentRoot: string, buildCommand: string | undefined) =>
|
||||
buildCommand ?? options.eveBuildCommand ?? createDefaultBuildCommand({ agentRoot });
|
||||
|
||||
if (options.agents === undefined) {
|
||||
if (options.eveRoot === undefined) {
|
||||
const context = await findEveProjectContext(nextRoot);
|
||||
if (
|
||||
context?.kind === "workspace" &&
|
||||
context.workspace.root === nextRoot &&
|
||||
context.workspace.members.length > 0
|
||||
) {
|
||||
return context.workspace.members.map((member, index) => ({
|
||||
appRoot: member.appRoot,
|
||||
buildCommand: resolveBuildCommand(member.appRoot, undefined),
|
||||
localProductionPortOffset: index,
|
||||
name: member.name,
|
||||
publicRoutePrefix: createNamedAgentRoutePrefix(member.name),
|
||||
servicePrefix: createNamedAgentServicePrefix(servicePrefixBase, member.name),
|
||||
workspaceMember: true,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const appRoot = resolveApplicationRoot(options.eveRoot);
|
||||
return [
|
||||
{
|
||||
@@ -339,10 +377,6 @@ function normalizeAgentsConfig(options: WithEveOptions): readonly ResolvedEveNex
|
||||
];
|
||||
}
|
||||
|
||||
if (options.eveRoot !== undefined) {
|
||||
throw new Error("withEve cannot combine eveRoot with agents. Use one configuration form.");
|
||||
}
|
||||
|
||||
const entries = Object.entries(options.agents);
|
||||
if (entries.length === 0) {
|
||||
throw new Error("withEve agents must contain at least one named eve agent.");
|
||||
@@ -385,19 +419,34 @@ export function withEve<TConfig extends EveNextConfig>(
|
||||
): EveNextConfigFunction<TConfig> {
|
||||
const nextRoot = process.cwd();
|
||||
const devServerTimeoutMs = resolveDevServerTimeout(options.devServerTimeoutMs);
|
||||
const agents = normalizeAgentsConfig(options);
|
||||
|
||||
assertValidWithEveOptions(options);
|
||||
return async function eveNextConfig(phase, context) {
|
||||
const nextConfig = await resolveNextConfig(configOrFunction, phase, context);
|
||||
const [agents, nextConfig] = await Promise.all([
|
||||
normalizeAgentsConfig(options, nextRoot),
|
||||
resolveNextConfig(configOrFunction, phase, context),
|
||||
]);
|
||||
const existingRewrites = nextConfig.rewrites;
|
||||
const configuredVercel = await ensureEveVercelOutputConfig({
|
||||
agents: agents.map((agent) => ({
|
||||
appRoot: agent.appRoot,
|
||||
buildCommand: agent.buildCommand,
|
||||
name: agent.name,
|
||||
publicRoutePrefix: agent.publicRoutePrefix,
|
||||
servicePrefix: agent.servicePrefix,
|
||||
})),
|
||||
agents: agents.map((agent) => {
|
||||
const outputAgent: {
|
||||
appRoot: string;
|
||||
buildCommand: string;
|
||||
name?: string;
|
||||
publicRoutePrefix: string;
|
||||
servicePrefix: string;
|
||||
workspaceMember?: boolean;
|
||||
} = {
|
||||
appRoot: agent.appRoot,
|
||||
buildCommand: agent.buildCommand,
|
||||
name: agent.name,
|
||||
publicRoutePrefix: agent.publicRoutePrefix,
|
||||
servicePrefix: agent.servicePrefix,
|
||||
};
|
||||
if (agent.workspaceMember === true) {
|
||||
outputAgent.workspaceMember = true;
|
||||
}
|
||||
return outputAgent;
|
||||
}),
|
||||
nextRoot,
|
||||
});
|
||||
|
||||
|
||||
@@ -51,6 +51,21 @@ describe("resolveEveDestinationPrefix", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("reports a missing local production build instead of proxying to an unstarted port", async () => {
|
||||
vi.stubEnv("NODE_ENV", "production");
|
||||
const appRoot = await createTempAppRoot();
|
||||
|
||||
await expect(
|
||||
resolveEveDestinationPrefix({
|
||||
appRoot,
|
||||
phase: "phase-production-server",
|
||||
productionDestinationPrefix: "http://127.0.0.1:4274",
|
||||
productionServerOrigin: "http://127.0.0.1:4274",
|
||||
}),
|
||||
).rejects.toThrow(`Run eve build from ${appRoot} before starting Next.js.`);
|
||||
expect(spawnMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores non-server URLs in dev server output while waiting for the listening URL", async () => {
|
||||
vi.stubEnv("NODE_ENV", "development");
|
||||
const appRoot = await createTempAppRoot();
|
||||
|
||||
@@ -451,13 +451,15 @@ function startEveDevServer(
|
||||
function startEveProductionServer(input: {
|
||||
readonly appRoot: string;
|
||||
readonly origin: string;
|
||||
}): Promise<EveProcessHandle> | undefined {
|
||||
}): Promise<EveProcessHandle> {
|
||||
const parsedOrigin = new URL(input.origin);
|
||||
const port = parsedOrigin.port;
|
||||
const serverEntry = join(input.appRoot, ".output", "server", "index.mjs");
|
||||
|
||||
if (!existsSync(serverEntry)) {
|
||||
return undefined;
|
||||
throw new Error(
|
||||
`eve production output is missing at ${serverEntry}. Run eve build from ${input.appRoot} before starting Next.js.`,
|
||||
);
|
||||
}
|
||||
|
||||
return startServerProcess({
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface EnsureVercelOutputConfigAgentInput {
|
||||
readonly name?: string;
|
||||
readonly publicRoutePrefix: string;
|
||||
readonly servicePrefix: string;
|
||||
readonly workspaceMember?: boolean;
|
||||
}
|
||||
|
||||
export interface EnsureVercelOutputConfigAgentResult {
|
||||
|
||||
Reference in New Issue
Block a user