eve 0.12.2 → 0.13.0
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/CHANGELOG.md +25 -0
- package/dist/src/cli/commands/agent-instructions.d.ts +15 -3
- package/dist/src/cli/commands/agent-instructions.js +3 -1
- package/dist/src/cli/commands/agent-prompt/build-and-verify.md +33 -0
- package/dist/src/cli/commands/agent-prompt/collect-intent.md +18 -0
- package/dist/src/cli/commands/agent-prompt/intro-handoff.md +4 -0
- package/dist/src/cli/commands/agent-prompt/intro-setup.md +5 -0
- package/dist/src/cli/commands/agent-prompt/scaffold.md +19 -0
- package/dist/src/cli/commands/agent-prompt/vercel-connect.md +3 -0
- package/dist/src/cli/commands/deploy.js +1 -1
- package/dist/src/cli/commands/init-repl.d.ts +79 -0
- package/dist/src/cli/commands/init-repl.js +1 -0
- package/dist/src/cli/commands/init.d.ts +6 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/link.d.ts +2 -2
- package/dist/src/cli/commands/link.js +1 -1
- package/dist/src/cli/dev/tui/line-editor.d.ts +2 -0
- package/dist/src/cli/dev/tui/line-editor.js +2 -2
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +26 -7
- package/dist/src/cli/dev/tui/setup-commands.js +2 -2
- package/dist/src/cli/dev/tui/setup-flow.d.ts +10 -3
- package/dist/src/cli/dev/tui/setup-panel.d.ts +16 -8
- package/dist/src/cli/dev/tui/setup-panel.js +3 -3
- package/dist/src/cli/dev/tui/setup-selection-input.d.ts +2 -1
- package/dist/src/cli/dev/tui/setup-selection-input.js +2 -2
- package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
- package/dist/src/cli/dev/tui/terminal-text.d.ts +11 -0
- package/dist/src/cli/dev/tui/terminal-text.js +1 -1
- package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
- package/dist/src/cli/run.d.ts +1 -0
- package/dist/src/cli/run.js +2 -2
- package/dist/src/cli/ui/live-row.js +1 -1
- package/dist/src/cli/ui/progress-pulse.d.ts +10 -0
- package/dist/src/cli/ui/progress-pulse.js +1 -0
- package/dist/src/discover/discover-agent.js +1 -1
- package/dist/src/evals/target.js +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-sdk-types.d.ts +0 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/multiplexed-command.d.ts +22 -0
- package/dist/src/execution/sandbox/multiplexed-command.js +1 -0
- package/dist/src/execution/tool-auth.d.ts +17 -30
- package/dist/src/execution/tool-auth.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-module-loader.js +2 -2
- package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
- package/dist/src/public/channels/github/api.d.ts +5 -0
- package/dist/src/public/channels/github/api.js +1 -1
- package/dist/src/public/channels/github/dispatch.js +1 -1
- package/dist/src/public/channels/github/pr-context.d.ts +10 -0
- package/dist/src/public/channels/github/pr-context.js +2 -2
- package/dist/src/public/channels/slack/connections.d.ts +16 -18
- package/dist/src/public/channels/slack/defaults.d.ts +1 -1
- package/dist/src/public/channels/slack/defaults.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +6 -7
- package/dist/src/public/definitions/tool.d.ts +40 -41
- package/dist/src/public/definitions/tool.js +1 -1
- package/dist/src/public/tools/index.d.ts +1 -1
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/connections/validate-authorization.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/types.d.ts +0 -10
- package/dist/src/setup/ai-gateway-api-key.d.ts +15 -0
- package/dist/src/setup/ai-gateway-api-key.js +1 -0
- package/dist/src/setup/boxes/apply-ai-gateway-credential.js +1 -1
- package/dist/src/setup/boxes/detect-ai-gateway.js +2 -2
- package/dist/src/setup/boxes/link-project.d.ts +8 -14
- package/dist/src/setup/boxes/link-project.js +1 -1
- package/dist/src/setup/boxes/resolve-provisioning.d.ts +2 -1
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/cli/select-component.d.ts +7 -1
- package/dist/src/setup/cli/select-component.js +1 -1
- package/dist/src/setup/cli/select-option-codec.d.ts +1 -0
- package/dist/src/setup/cli/select-option-codec.js +1 -1
- package/dist/src/setup/cli/select-state.d.ts +14 -1
- package/dist/src/setup/cli/select-state.js +1 -1
- package/dist/src/setup/flows/channels.js +1 -1
- package/dist/src/setup/flows/deploy.js +1 -1
- package/dist/src/setup/flows/install-vercel-cli.js +1 -1
- package/dist/src/setup/flows/link.d.ts +2 -1
- package/dist/src/setup/flows/link.js +1 -1
- package/dist/src/setup/flows/login.js +1 -1
- package/dist/src/setup/flows/model.d.ts +5 -4
- package/dist/src/setup/flows/model.js +1 -1
- package/dist/src/setup/flows/vercel.js +2 -2
- package/dist/src/setup/index.d.ts +1 -1
- package/dist/src/setup/package-manager.d.ts +2 -1
- package/dist/src/setup/package-manager.js +1 -1
- package/dist/src/setup/primitives/pm/index.d.ts +1 -1
- package/dist/src/setup/primitives/pm/pnpm.d.ts +11 -1
- package/dist/src/setup/primitives/pm/pnpm.js +5 -2
- package/dist/src/setup/primitives/pm/types.d.ts +10 -1
- package/dist/src/setup/project-resolution.d.ts +9 -6
- package/dist/src/setup/project-resolution.js +1 -1
- package/dist/src/setup/prompter.d.ts +13 -0
- package/dist/src/setup/prompter.js +1 -1
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.d.ts +8 -0
- package/dist/src/setup/scaffold/create/project.js +8 -7
- package/dist/src/setup/scaffold/update/channels.d.ts +8 -0
- package/dist/src/setup/scaffold/update/channels.js +2 -2
- package/dist/src/setup/scaffold/update/package-json.d.ts +3 -0
- package/dist/src/setup/scaffold/update/package-json.js +1 -1
- package/dist/src/setup/scaffold/workspace-glob.d.ts +5 -0
- package/dist/src/setup/scaffold/workspace-glob.js +1 -0
- package/dist/src/setup/scaffold/workspace-root.d.ts +29 -0
- package/dist/src/setup/scaffold/workspace-root.js +1 -0
- package/dist/src/setup/state.d.ts +7 -2
- package/dist/src/setup/vercel-api-failure.d.ts +9 -0
- package/dist/src/setup/vercel-api-failure.js +1 -0
- package/dist/src/setup/vercel-deployment.js +1 -1
- package/dist/src/setup/vercel-project-api.d.ts +25 -0
- package/dist/src/setup/vercel-project-api.js +1 -0
- package/dist/src/setup/vercel-project.d.ts +11 -57
- package/dist/src/setup/vercel-project.js +1 -1
- package/dist/src/setup/with-spinner.d.ts +3 -0
- package/dist/src/setup/with-spinner.js +1 -0
- package/dist/src/shared/package-name.d.ts +4 -0
- package/dist/src/shared/package-name.js +1 -0
- package/docs/channels/slack.mdx +1 -1
- package/docs/connections.mdx +9 -6
- package/docs/getting-started.mdx +8 -3
- package/docs/guides/auth-and-route-protection.md +57 -10
- package/docs/guides/dev-tui.md +3 -3
- package/docs/reference/cli.md +3 -3
- package/docs/reference/typescript-api.md +7 -7
- package/package.json +1 -1
- package/dist/src/cli/commands/init-agent-handoff.md +0 -12
- package/dist/src/cli/commands/init-agent-instructions.md +0 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# eve
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 306e14e: Remove the top-level `auth` field from `defineTool()` and require tool auth providers to be passed inline to `ctx.getToken(provider)` or `ctx.requireAuth(provider)`.
|
|
8
|
+
- f00ca73: Search every Vercel project in the selected team and preserve the selected project ID through linking.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- 36b67fc: Make `eve init` respect ancestor package-manager workspaces when scaffolding nested packages. The scaffold now updates workspace-owned package policy at the npm, pnpm, Yarn, or Bun workspace root instead of writing nested root-only config into the generated package.
|
|
13
|
+
|
|
14
|
+
## 0.12.3
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 680ff48: Text prompts now use block cursors, while active turns and model or channel setup use shared green progress pulses.
|
|
19
|
+
- 27a9701: Resolve extensionless relative imports whose target basename contains dots when bundling authored modules. Local files such as `./mock-registry.schemas` and dependency requires such as `./Reflect.getPrototypeOf` now probe Eve's configured `.ts` and `.js` extensions before being treated as asset imports.
|
|
20
|
+
- 3a64a8f: `eve init` with no target, when run by a coding agent, now prints a setup guide — what to ask the user, then the scaffold command — instead of scaffolding the current directory. The guide routes both channels (Slack credentials) and connections (per-user OAuth) through Vercel Connect so credentials are provisioned rather than hand-managed. `eve init <name>` and `eve init .` are unchanged.
|
|
21
|
+
- 3a64a8f: `eve init` now offers to open an installed coding-agent REPL when its CLI is on `PATH`, while keeping `eve dev` as the default. It detects Claude Code, Codex, Cursor, Droid, Gemini CLI, opencode, and Pi. The selected REPL starts with a project-specific setup prompt and `eve dev --no-ui` verification guidance. Coding-agent and non-interactive launches, plus systems without any supported CLI, keep the existing development-server handoff.
|
|
22
|
+
- 86a35eb: Add inline tool auth provider overloads so tools can call `ctx.getToken(provider, options?)` and `ctx.requireAuth(provider, options?)` without declaring a single top-level `auth`. Vercel Connect providers can be authored inline with `connect("service/agent")` or `connect({ connector, tokenParams })`; the existing top-level tool `auth` field and no-argument tool auth accessors remain supported for compatibility, but are now deprecated in favor of inline providers.
|
|
23
|
+
- 25ab1e7: Preserve dev-runtime snapshots that are still referenced by local durable workflow data so parked HITL turns can resume after `eve dev` rebuilds.
|
|
24
|
+
- 504f59e: Allow `eve eval` target checks to match a scoped package name such as `@acme/agent` against the runtime agent identity `agent`.
|
|
25
|
+
- 0dca794: Restore Slack authorization status updates by posting a link-free public status while sending the sign-in challenge privately, then updating the public status when authorization completes.
|
|
26
|
+
- 3548363: Strengthen Vercel and just-bash process streaming with deterministic completion, safe output cancellation, and idempotent process operations.
|
|
27
|
+
|
|
3
28
|
## 0.12.2
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/** Ordered `agent-prompt/` sections composed into the pre-scaffold setup guide. */
|
|
2
|
+
export declare const SETUP_SECTIONS: readonly ["intro-setup.md", "collect-intent.md", "vercel-connect.md", "scaffold.md", "build-and-verify.md"];
|
|
3
|
+
/** Ordered `agent-prompt/` sections composed into the post-scaffold handoff. */
|
|
4
|
+
export declare const HANDOFF_SECTIONS: readonly ["intro-handoff.md", "collect-intent.md", "vercel-connect.md", "build-and-verify.md"];
|
|
5
|
+
/**
|
|
6
|
+
* The pre-scaffold setup guide, shown when a coding agent runs a bare
|
|
7
|
+
* `eve init`. It scaffolds from scratch, so it renders with the universal
|
|
8
|
+
* `npx eve dev` rather than a launcher-specific command.
|
|
9
|
+
*/
|
|
10
|
+
export declare function initAgentInstructions(): string;
|
|
11
|
+
/** The post-scaffold handoff printed after a coding agent runs `eve init <name>`. */
|
|
4
12
|
export declare function initAgentDevHandoff(options: {
|
|
5
13
|
projectPath: string;
|
|
6
14
|
devCommand: string;
|
|
7
15
|
}): string;
|
|
16
|
+
/** The initial prompt for a coding-agent REPL opened inside the scaffolded project. */
|
|
17
|
+
export declare function initAgentReplPrompt(options: {
|
|
18
|
+
devCommand: string;
|
|
19
|
+
}): string;
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
import{readFileSync}from"node:fs";function
|
|
1
|
+
import{readFileSync}from"node:fs";const SETUP_SECTIONS=[`intro-setup.md`,`collect-intent.md`,`vercel-connect.md`,`scaffold.md`,`build-and-verify.md`],HANDOFF_SECTIONS=[`intro-handoff.md`,`collect-intent.md`,`vercel-connect.md`,`build-and-verify.md`];function compose(t,n){let r=t.map(t=>readFileSync(new URL(`./agent-prompt/${t}`,import.meta.url),`utf8`).trim()).join(`
|
|
2
|
+
|
|
3
|
+
`).replaceAll(`{{devCommand}}`,()=>n.devCommand),{workingDirectory:i}=n;return i===void 0?r:r.replaceAll(`{{workingDirectory}}`,()=>i)}function initAgentInstructions(){return compose(SETUP_SECTIONS,{devCommand:`npx eve dev`})}function initAgentDevHandoff(e){return compose(HANDOFF_SECTIONS,{devCommand:e.devCommand,workingDirectory:e.projectPath})}function initAgentReplPrompt(e){return compose(HANDOFF_SECTIONS,{devCommand:e.devCommand,workingDirectory:`.`})}export{HANDOFF_SECTIONS,SETUP_SECTIONS,initAgentDevHandoff,initAgentInstructions,initAgentReplPrompt};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## Build it out, then verify
|
|
2
|
+
|
|
3
|
+
Work from the project directory. Once eve is installed, the full docs are bundled
|
|
4
|
+
at `node_modules/eve/docs/` and match the installed version exactly. Read
|
|
5
|
+
`README.md` there first, then the guide for what you're adding, such as
|
|
6
|
+
`connections`, `channels/slack`, or `guides/auth-and-route-protection` for the
|
|
7
|
+
Vercel Connect flow.
|
|
8
|
+
|
|
9
|
+
- Put the purpose in `agent/instructions.md` (the always-on system prompt),
|
|
10
|
+
replacing the scaffold's placeholder with what the user said the agent should
|
|
11
|
+
do.
|
|
12
|
+
- Add a first typed tool under `agent/tools/` with `defineTool` from `eve/tools`
|
|
13
|
+
and a Zod `inputSchema`.
|
|
14
|
+
|
|
15
|
+
`{{devCommand}}` starts eve's HMR development server and opens the agent's
|
|
16
|
+
terminal REPL. It does not start or control this coding-agent session, so don't
|
|
17
|
+
use the bare command as a background verification process. Start eve without the
|
|
18
|
+
terminal UI in a controllable background process instead:
|
|
19
|
+
|
|
20
|
+
{{devCommand}} --no-ui
|
|
21
|
+
|
|
22
|
+
Wait for the server URL, then exercise the HTTP API: create a session with
|
|
23
|
+
`POST /eve/v1/session`, attach to `GET /eve/v1/session/:id/stream`, and send a
|
|
24
|
+
follow-up with the returned `continuationToken`. Stop the dev process after
|
|
25
|
+
verification.
|
|
26
|
+
|
|
27
|
+
When the user is ready to use their agent's REPL, give them the interactive
|
|
28
|
+
command to run from the project directory:
|
|
29
|
+
|
|
30
|
+
{{devCommand}}
|
|
31
|
+
|
|
32
|
+
Verify the project's typecheck passes, adapt the model and provider to the user's
|
|
33
|
+
data and use case, and don't commit unless the user asks.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## Collect intent
|
|
2
|
+
|
|
3
|
+
If the user's intent is not already clear, ask the questions one at a time, use
|
|
4
|
+
the coding harness's prompt tools when available, and do not guess.
|
|
5
|
+
|
|
6
|
+
1. What should the agent do? This becomes its always-on purpose.
|
|
7
|
+
2. Where should it be reachable? Every agent ships the built-in HTTP channel. On
|
|
8
|
+
top of that:
|
|
9
|
+
- **Web Chat** (a Next.js app): add it at init with `--channel-web-nextjs`.
|
|
10
|
+
- **Slack** and other platforms: add after deploy with `eve channels add slack`.
|
|
11
|
+
Credentials run through **Vercel Connect**, which provisions the bot token
|
|
12
|
+
and verifies inbound webhooks, so there is no `SLACK_BOT_TOKEN` or signing
|
|
13
|
+
secret to manage.
|
|
14
|
+
3. Which external systems does it need programmatic read/write access to, such as
|
|
15
|
+
Slack, Salesforce, Linear, GitHub, or your own API? Each becomes a connection
|
|
16
|
+
under `agent/connections/`. When a system needs every end-user to sign in, wire
|
|
17
|
+
its auth through **Vercel Connect** (`connect()` from `@vercel/connect/eve`),
|
|
18
|
+
which handles consent, encrypted token storage, and refresh.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Scaffold
|
|
2
|
+
|
|
3
|
+
First settle the target: a new project, or the agent added to an existing
|
|
4
|
+
directory? For a new project, propose a name and ask the user to confirm it; for
|
|
5
|
+
an existing one, ask for the directory.
|
|
6
|
+
|
|
7
|
+
For a new project, run (append `--channel-web-nextjs` only if the user wants Web
|
|
8
|
+
Chat):
|
|
9
|
+
|
|
10
|
+
npx eve@latest init <name>
|
|
11
|
+
|
|
12
|
+
This creates the project, installs its dependencies, and initializes Git. Since a
|
|
13
|
+
coding agent launched init, it prints a development handoff instead of starting
|
|
14
|
+
the interactive terminal UI.
|
|
15
|
+
|
|
16
|
+
For an existing app, run `npx eve@latest init .` from its directory. This adds the
|
|
17
|
+
agent and missing dependencies while leaving the existing Git repository and app
|
|
18
|
+
scripts alone. If init cannot be used, install by hand with
|
|
19
|
+
`npm install eve@latest ai zod`; manual installation does not add package scripts.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NOT_AN_AGENT_MESSAGE,hasInteractiveTerminal}from"./preconditions.js";import{isEveProject}from"#setup/scaffold/index.js";import{createPrompter}from"#setup/prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";const defaultDependencies={isEveProject,hasInteractiveTerminal};async function runDeployCommand(t,n,r=defaultDependencies){if(!await r.isEveProject(n)){t.error(NOT_AN_AGENT_MESSAGE),process.exitCode=1;return}let i=r.createPrompter?.()??createPrompter();i.intro(`Deploy your eve agent to Vercel`);try{let e=await runDeployFlow({appRoot:n,prompter:i,interactive:r.hasInteractiveTerminal(),deps:r.flowDeps});if(e.kind===`needs-link`){t.error("This directory is not linked to a Vercel project. Run `eve link` first (or `vercel link --project <name> --yes` in CI), then re-run `eve deploy`."),process.exitCode=1;return}i.outro(e.kind===`cancelled`?`Cancelled.`:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`)}catch(e){t.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}export{runDeployCommand};
|
|
1
|
+
import{NOT_AN_AGENT_MESSAGE,hasInteractiveTerminal}from"./preconditions.js";import{isEveProject}from"#setup/scaffold/index.js";import{createPrompter}from"#setup/prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";const defaultDependencies={isEveProject,hasInteractiveTerminal};async function runDeployCommand(t,n,r=defaultDependencies){if(!await r.isEveProject(n)){t.error(NOT_AN_AGENT_MESSAGE),process.exitCode=1;return}let i=r.createPrompter?.()??createPrompter();i.intro(`Deploy your eve agent to Vercel`);try{let e=await runDeployFlow({appRoot:n,prompter:i,interactive:r.hasInteractiveTerminal(),deps:r.flowDeps});if(e.kind===`needs-link`){t.error("This directory is not linked to a Vercel project. Run `eve link` first (or `vercel link --project <name> --yes --non-interactive` in CI), then re-run `eve deploy`."),process.exitCode=1;return}i.outro(e.kind===`cancelled`?`Cancelled.`:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`)}catch(e){t.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}export{runDeployCommand};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Prompter } from "#setup/prompter.js";
|
|
2
|
+
declare const CODING_AGENT_REPLS: readonly [{
|
|
3
|
+
readonly command: "claude";
|
|
4
|
+
readonly label: "Claude Code";
|
|
5
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
6
|
+
}, {
|
|
7
|
+
readonly command: "codex";
|
|
8
|
+
readonly label: "Codex";
|
|
9
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
10
|
+
}, {
|
|
11
|
+
readonly command: "cursor-agent";
|
|
12
|
+
readonly label: "Cursor";
|
|
13
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
14
|
+
}, {
|
|
15
|
+
readonly command: "droid";
|
|
16
|
+
readonly label: "Droid";
|
|
17
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
18
|
+
}, {
|
|
19
|
+
readonly command: "gemini";
|
|
20
|
+
readonly label: "Gemini CLI";
|
|
21
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
22
|
+
}, {
|
|
23
|
+
readonly command: "opencode";
|
|
24
|
+
readonly label: "opencode";
|
|
25
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
26
|
+
}, {
|
|
27
|
+
readonly command: "pi";
|
|
28
|
+
readonly label: "Pi";
|
|
29
|
+
readonly promptArgs: (prompt: string) => string[];
|
|
30
|
+
}];
|
|
31
|
+
type CodingAgentReplDefinition = (typeof CODING_AGENT_REPLS)[number];
|
|
32
|
+
/** A coding-agent REPL that can take over the terminal after `eve init`. */
|
|
33
|
+
export type CodingAgentRepl = CodingAgentReplDefinition["command"];
|
|
34
|
+
/** The one post-init continuation point for a human terminal session. */
|
|
35
|
+
export type InitHandoff = "eve-dev" | CodingAgentRepl;
|
|
36
|
+
export interface InitReplDependencies {
|
|
37
|
+
createPrompter(): Prompter;
|
|
38
|
+
hasInteractiveTerminal(): boolean;
|
|
39
|
+
isCodingAgentReplAvailable(command: CodingAgentRepl): Promise<boolean>;
|
|
40
|
+
}
|
|
41
|
+
/** The full path of the named REPL's executable on `PATH`, or null if absent. */
|
|
42
|
+
export declare function resolveCodingAgentRepl(command: CodingAgentRepl): Promise<string | null>;
|
|
43
|
+
/** True when the named REPL resolves to an executable on the current `PATH`. */
|
|
44
|
+
export declare function isCodingAgentReplAvailable(command: CodingAgentRepl): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Offers any locally installed coding-agent REPLs immediately before the
|
|
47
|
+
* existing `eve dev` handoff. Non-interactive sessions, and systems with none
|
|
48
|
+
* of them on `PATH`, keep the prior direct-to-dev behavior.
|
|
49
|
+
*/
|
|
50
|
+
export declare function selectInitHandoff(input: {
|
|
51
|
+
agentName: string;
|
|
52
|
+
deps?: Partial<InitReplDependencies>;
|
|
53
|
+
}): Promise<InitHandoff>;
|
|
54
|
+
interface ReplSpawnPlan {
|
|
55
|
+
file: string;
|
|
56
|
+
args: readonly string[];
|
|
57
|
+
shell: boolean;
|
|
58
|
+
/** False means the prompt is not in argv and the caller must surface it. */
|
|
59
|
+
seeded: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Decides how to launch a REPL from its resolved executable path. A directly
|
|
63
|
+
* spawnable executable runs without a shell, so the multi-line prompt passes
|
|
64
|
+
* through argv verbatim. A `.cmd`/`.bat` shim (or an unresolved command) can
|
|
65
|
+
* only run via cmd.exe, which cannot carry the prompt's newlines or
|
|
66
|
+
* metacharacters in an argument, so it launches the bare REPL and reports the
|
|
67
|
+
* prompt as unseeded.
|
|
68
|
+
*/
|
|
69
|
+
export declare function replSpawnPlan(command: CodingAgentRepl, resolvedPath: string | null, prompt: string, platform?: NodeJS.Platform): ReplSpawnPlan;
|
|
70
|
+
/** Starts the selected coding-agent REPL in the newly initialized project. */
|
|
71
|
+
export declare function spawnCodingAgentRepl(input: {
|
|
72
|
+
command: CodingAgentRepl;
|
|
73
|
+
cwd: string;
|
|
74
|
+
prompt: string;
|
|
75
|
+
/** Surfaces the prompt when it could not be seeded into the REPL's argv. */
|
|
76
|
+
onPromptUnseeded?: (prompt: string) => void;
|
|
77
|
+
resolvePath?: (command: CodingAgentRepl) => Promise<string | null>;
|
|
78
|
+
}): Promise<boolean>;
|
|
79
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{hasInteractiveTerminal}from"./preconditions.js";import{constants}from"node:fs";import{delimiter,extname,join}from"node:path";import{createPrompter}from"#setup/prompter.js";import{spawn}from"node:child_process";import{access,stat}from"node:fs/promises";const positional=e=>[e],CODING_AGENT_REPLS=[{command:`claude`,label:`Claude Code`,promptArgs:positional},{command:`codex`,label:`Codex`,promptArgs:positional},{command:`cursor-agent`,label:`Cursor`,promptArgs:positional},{command:`droid`,label:`Droid`,promptArgs:positional},{command:`gemini`,label:`Gemini CLI`,promptArgs:e=>[`-i`,e]},{command:`opencode`,label:`opencode`,promptArgs:e=>[`--prompt`,e]},{command:`pi`,label:`Pi`,promptArgs:positional}],WINDOWS_EXECUTABLE_EXTENSIONS=[`.COM`,`.EXE`,`.BAT`,`.CMD`],defaultDependencies={createPrompter,hasInteractiveTerminal,isCodingAgentReplAvailable};function executableNames(e){if(process.platform!==`win32`)return[e];let t=process.env.PATHEXT?.split(`;`).filter(Boolean);return(t&&t.length>0?t:WINDOWS_EXECUTABLE_EXTENSIONS).map(t=>`${e}${t}`)}async function isExecutable(e){try{return(await stat(e)).isFile()?(await access(e,constants.X_OK),!0):!1}catch{return!1}}async function resolveCodingAgentRepl(e){let t=process.env.PATH;if(t===void 0||t.length===0)return null;for(let r of t.split(delimiter))if(r.length!==0)for(let t of executableNames(e)){let e=join(r,t);if(await isExecutable(e))return e}return null}async function isCodingAgentReplAvailable(e){return await resolveCodingAgentRepl(e)!==null}function handoffOptions(e,t){return[{value:`eve-dev`,label:`Start eve dev`,focusHint:`talk to '${t}' in your terminal`},...e.map(e=>({value:e.command,label:`Open ${e.label}`,focusHint:`build '${t}' using ${e.label}`}))]}async function selectInitHandoff(e){let t={...defaultDependencies,...e.deps};if(!t.hasInteractiveTerminal())return`eve-dev`;let n=await Promise.all(CODING_AGENT_REPLS.map(({command:e})=>t.isCodingAgentReplAvailable(e))),r=CODING_AGENT_REPLS.filter((e,t)=>n[t]);return r.length===0?`eve-dev`:t.createPrompter().select({message:`How would you like to continue?`,options:handoffOptions(r,e.agentName),initialValue:`eve-dev`})}function codingAgentRepl(e){let t=CODING_AGENT_REPLS.find(t=>t.command===e);if(t===void 0)throw Error(`Unsupported coding-agent REPL "${e}".`);return t}function isDirectlySpawnable(e,t){if(t!==`win32`)return!0;let n=extname(e).toLowerCase();return n===`.exe`||n===`.com`}function replSpawnPlan(e,t,n,r=process.platform){return t!==null&&isDirectlySpawnable(t,r)?{file:t,args:codingAgentRepl(e).promptArgs(n),shell:!1,seeded:!0}:{file:e,args:[],shell:r===`win32`,seeded:!1}}async function spawnCodingAgentRepl(e){let t=e.resolvePath??resolveCodingAgentRepl,n=replSpawnPlan(e.command,await t(e.command),e.prompt);return n.seeded||e.onPromptUnseeded?.(e.prompt),new Promise(t=>{let r=spawn(n.file,n.args,{cwd:e.cwd,shell:n.shell,stdio:`inherit`}),i=!1,settle=e=>{i||(i=!0,t(e))};r.once(`error`,()=>settle(!1)),r.once(`close`,e=>settle(e===0))})}export{isCodingAgentReplAvailable,replSpawnPlan,resolveCodingAgentRepl,selectInitHandoff,spawnCodingAgentRepl};
|
|
@@ -4,6 +4,7 @@ import { runPackageManagerInstall, spawnPackageManager } from "#setup/primitives
|
|
|
4
4
|
import { addAgentToProject } from "#setup/scaffold/create/add-to-project.js";
|
|
5
5
|
import { ensureChannel, scaffoldBaseProject } from "#setup/scaffold/index.js";
|
|
6
6
|
import { tryInitializeGit } from "./init-git.js";
|
|
7
|
+
import { selectInitHandoff, spawnCodingAgentRepl } from "./init-repl.js";
|
|
7
8
|
export interface InitCliLogger {
|
|
8
9
|
error(message: string): void;
|
|
9
10
|
log(message: string): void;
|
|
@@ -21,6 +22,8 @@ export interface InitCommandDependencies {
|
|
|
21
22
|
now: () => number;
|
|
22
23
|
runPackageManagerInstall: typeof runPackageManagerInstall;
|
|
23
24
|
scaffoldBaseProject: typeof scaffoldBaseProject;
|
|
25
|
+
selectInitHandoff: typeof selectInitHandoff;
|
|
26
|
+
spawnCodingAgentRepl: typeof spawnCodingAgentRepl;
|
|
24
27
|
spawnPackageManager: typeof spawnPackageManager;
|
|
25
28
|
tryInitializeGit: typeof tryInitializeGit;
|
|
26
29
|
}
|
|
@@ -32,5 +35,8 @@ export declare const EVE_INIT_PACKAGE_SPEC_ENV = "EVE_INIT_PACKAGE_SPEC";
|
|
|
32
35
|
*
|
|
33
36
|
* Runs launched by a coding agent get the dev command printed instead of
|
|
34
37
|
* spawned after scaffolding, since the dev TUI would wedge the launching agent.
|
|
38
|
+
* A coding agent that omits the target entirely gets the setup guide printed and
|
|
39
|
+
* nothing scaffolded, since a bare `eve init` means it has not yet chosen what to
|
|
40
|
+
* build.
|
|
35
41
|
*/
|
|
36
42
|
export declare function runInitCommand(logger: InitCliLogger, parentDirectory: string, target: string | undefined, options: InitCommandOptions, dependencies?: InitCommandDependencies): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{initAgentDevHandoff}from"./agent-instructions.js";import{tryInitializeGit}from"./init-git.js";import{createLogger,isLogLevelEnabled}from"#internal/logging.js";import{basename,join,resolve}from"node:path";import{ensureChannel,scaffoldBaseProject}from"#setup/scaffold/index.js";import{mkdtemp,readdir,rename,rm,stat}from"node:fs/promises";import{performance}from"node:perf_hooks";import{isCodingAgentLaunch}from"#cli/agent-detection.js";import{EVE_WORDMARK}from"#cli/banner.js";import{formatElapsed}from"#cli/format-elapsed.js";import{startCliLiveRow}from"#cli/ui/live-row.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectInvokingPackageManager,detectPackageManager}from"#setup/package-manager.js";import{pathExists}from"#setup/path-exists.js";import{parseProjectName}from"#setup/project-name.js";import{eveDevArguments,runPackageManagerInstall,spawnPackageManager}from"#setup/primitives/index.js";import{addAgentToProject}from"#setup/scaffold/create/add-to-project.js";import{DEFAULT_EVE_PACKAGE_CONTRACT}from"#setup/scaffold/create/project.js";var import_picocolors=__toESM(require_picocolors(),1);const defaultDependencies={addAgentToProject,detectInvokingPackageManager,detectPackageManager,ensureChannel,isCodingAgentLaunch,now:()=>performance.now(),runPackageManagerInstall,scaffoldBaseProject,spawnPackageManager,tryInitializeGit},ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),EVE_INIT_PACKAGE_SPEC_ENV=`EVE_INIT_PACKAGE_SPEC`,initLog=createLogger(`init`);async function resolveTargetDirectory(e,t){let n=resolve(e,t);return(await stat(n).catch(()=>void 0))?.isDirectory()?n:void 0}function isCurrentDirectoryTarget(e){return/^\.(?:[/\\]+\.?)*$/u.test(e.trim())}async function assertCanScaffoldInPlace(e){let t=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(t.length===0)return;let n=t.slice(0,5).join(`, `),r=t.length>5?`, and ${t.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${n}${r}. Use an empty directory.`)}async function moveDirectoryContents(e,t){for(let n of await readdir(e))await rename(join(e,n),join(t,n))}async function addToExistingProject(e,t,n,r){if(t.channelWebNextjs===!0)throw Error("`--channel-web-nextjs` is not supported when adding an agent to an existing project. Run `eve channels add web` from the project afterwards instead.");let i=await n.detectPackageManager(e),a=await n.addAgentToProject({projectRoot:e,model:DEFAULT_AGENT_MODEL_ID,packageManager:i.kind,evePackage:r});return{packageManager:i.kind,nodeEngineOverride:a.nodeEngineOverride}}async function resolveScaffoldPackageManager(e,t){let n=await t.detectPackageManager(e);return n.source==="default"?t.detectInvokingPackageManager()??`pnpm`:n.kind}async function scaffoldProject(e,t,n,r,i,a){let
|
|
1
|
+
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{initAgentDevHandoff,initAgentInstructions,initAgentReplPrompt}from"./agent-instructions.js";import{tryInitializeGit}from"./init-git.js";import{selectInitHandoff,spawnCodingAgentRepl}from"./init-repl.js";import{createLogger,isLogLevelEnabled}from"#internal/logging.js";import{basename,join,resolve}from"node:path";import{ensureChannel,scaffoldBaseProject}from"#setup/scaffold/index.js";import{mkdtemp,readdir,rename,rm,stat}from"node:fs/promises";import{performance}from"node:perf_hooks";import{isCodingAgentLaunch}from"#cli/agent-detection.js";import{EVE_WORDMARK}from"#cli/banner.js";import{formatElapsed}from"#cli/format-elapsed.js";import{startCliLiveRow}from"#cli/ui/live-row.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectInvokingPackageManager,detectPackageManager}from"#setup/package-manager.js";import{pathExists}from"#setup/path-exists.js";import{parseProjectName}from"#setup/project-name.js";import{eveDevArguments,runPackageManagerInstall,spawnPackageManager}from"#setup/primitives/index.js";import{addAgentToProject}from"#setup/scaffold/create/add-to-project.js";import{WizardCancelledError}from"#setup/step.js";import{DEFAULT_EVE_PACKAGE_CONTRACT}from"#setup/scaffold/create/project.js";var import_picocolors=__toESM(require_picocolors(),1);const defaultDependencies={addAgentToProject,detectInvokingPackageManager,detectPackageManager,ensureChannel,isCodingAgentLaunch,now:()=>performance.now(),runPackageManagerInstall,scaffoldBaseProject,selectInitHandoff,spawnCodingAgentRepl,spawnPackageManager,tryInitializeGit},ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),EVE_INIT_PACKAGE_SPEC_ENV=`EVE_INIT_PACKAGE_SPEC`,initLog=createLogger(`init`);async function resolveTargetDirectory(e,t){let n=resolve(e,t);return(await stat(n).catch(()=>void 0))?.isDirectory()?n:void 0}function isCurrentDirectoryTarget(e){return/^\.(?:[/\\]+\.?)*$/u.test(e.trim())}async function assertCanScaffoldInPlace(e){let t=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(t.length===0)return;let n=t.slice(0,5).join(`, `),r=t.length>5?`, and ${t.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${n}${r}. Use an empty directory.`)}async function moveDirectoryContents(e,t){for(let n of await readdir(e))await rename(join(e,n),join(t,n))}function uniqueWorkspaceRootMutations(e){let t=new Map;for(let n of e){let e=`${n.kind}:${n.path}`,r=t.get(e);t.set(e,{...n,nodeEngineOverride:n.nodeEngineOverride??r?.nodeEngineOverride})}return[...t.values()]}function formatWorkspaceRootMutationWarning(e){let t=e.kind===`package-json`?`package.json`:`configuration`,n=e.nodeEngineOverride===void 0?``:` (${formatNodeEngineOverrideWarning(e.nodeEngineOverride)})`;return`Updated workspace root ${t} at ${e.path}${n}`}async function addToExistingProject(e,t,n,r){if(t.channelWebNextjs===!0)throw Error("`--channel-web-nextjs` is not supported when adding an agent to an existing project. Run `eve channels add web` from the project afterwards instead.");let i=await n.detectPackageManager(e),a=await n.addAgentToProject({projectRoot:e,model:DEFAULT_AGENT_MODEL_ID,packageManager:i.kind,evePackage:r});return{packageManager:i.kind,nodeEngineOverride:a.nodeEngineOverride}}async function resolveScaffoldPackageManager(e,t){let n=await t.detectPackageManager(e);return n.source==="default"?t.detectInvokingPackageManager()??`pnpm`:n.kind}async function scaffoldProject(e,t,n,r,i,a){let o=resolve(e),s=t===`.`,c=s?o:join(o,t);if(s)await assertCanScaffoldInPlace(c);else if(await pathExists(c))throw Error(`Cannot create project because "${c}" already exists.`);let l=await mkdtemp(join(o,`.eve-init-`)),p=[];try{let e={projectName:s?basename(c):t,model:DEFAULT_AGENT_MODEL_ID,evePackage:a,targetDirectory:l,workspaceProbeDirectory:c,packageManager:n,onWorkspaceRootMutation:e=>{p.push(e)}},o=await i.scaffoldBaseProject(e);return r.channelWebNextjs===!0&&await i.ensureChannel({projectRoot:o,kind:`web`,packageManager:n,workspaceProbeDirectory:c,configureVercelServices:!1,onWorkspaceRootMutation:e=>{p.push(e)}}),s?await moveDirectoryContents(o,c):await rename(o,c),{projectPath:c,workspaceRootMutations:uniqueWorkspaceRootMutations(p)}}finally{await rm(l,{recursive:!0,force:!0})}}function installProgressDetail(e,t){let n=t.text.trim();if(n===``||e!==`npm`)return n||void 0;let r=/^npm silly fetch manifest (.+)$/u.exec(n);if(r!==null)return`Resolving ${r[1]}`;let i=/^npm http fetch \S+ \S+ attempt (\d+) failed with (\S+)$/u.exec(n);if(i!==null)return`npm registry · attempt ${i[1]} failed: ${i[2]}`;if(t.stream===`stdout`||/^npm (?:error|warn)\b/u.test(n))return n}const NPM_NOISE_LINE=/^\s*npm (?:silly|verbose|http|timing)\b/u;async function runInitSteps(e){let{dependencies:t,logger:n,options:r,parentDirectory:i,target:a}=e,o=isLogLevelEnabled(`debug`),s=startCliLiveRow(n);s.update(`Preparing project`);try{let e=await t.isCodingAgentLaunch(),c=a??`.`,l=isCurrentDirectoryTarget(c),u=l?await pathExists(join(resolve(i),`package.json`))?resolve(i):void 0:await resolveTargetDirectory(i,c),p=resolveInitEvePackageOverride(),m=u===void 0?`creating agent`:`adding agent`;s.update(u===void 0?`Creating agent`:`Adding agent`),initLog.debug(m);let h=t.now(),g;if(u===void 0){let e=l?`.`:parseProjectName(c),n=resolve(i),a=await resolveScaffoldPackageManager(e===`.`?n:join(n,e),t),o=await scaffoldProject(i,e,a,r,t,p);g={kind:`created`,packageManager:a,projectPath:o.projectPath,workspaceRootMutations:o.workspaceRootMutations}}else{let e=await addToExistingProject(u,r,t,p);g=e.nodeEngineOverride===void 0?{kind:`added`,packageManager:e.packageManager,projectPath:u}:{kind:`added`,nodeEngineOverride:e.nodeEngineOverride,packageManager:e.packageManager,projectPath:u}}let _=t.now()-h;initLog.debug(`${m} done`,{ms:_}),s.update(`Installing dependencies`,`${g.packageManager} install`),initLog.debug(`installing dependencies with ${g.packageManager}`);let v=t.now(),y=[],b=[],x=await t.runPackageManagerInstall(g.packageManager,g.projectPath,{bypassMinimumReleaseAge:!0,progressDetails:process.stdout.isTTY===!0&&!o,onOutput:e=>{e.text.trim()!==``&&(b.push(e.text),b.length>20&&b.shift(),NPM_NOISE_LINE.test(e.text)||y.push(e.text)),o&&initLog.debug(e.text);let t=installProgressDetail(g.packageManager,e);t!==void 0&&s.update(`Installing dependencies`,t)}}),S=t.now()-v;if(!x){initLog.debug(`dependency installation failed`,{ms:S}),s.stop();let e=y.length>0?y:b;for(let t of e)n.error(t);throw Error(`Failed to install dependencies in "${g.projectPath}".`)}return initLog.debug(`dependencies installed`,{ms:S}),g.kind===`created`?(s.update(`Initializing Git repository`),initLog.debug(`initializing git repository`),{...g,agentElapsedMs:_,agentLaunched:e,gitResult:await t.tryInitializeGit(g.projectPath),installElapsedMs:S}):{...g,agentElapsedMs:_,agentLaunched:e,installElapsedMs:S}}finally{s.stop()}}async function runInitCommand(e,t,a,o,s=defaultDependencies){if(a===void 0&&await s.isCodingAgentLaunch()){e.log(initAgentInstructions());return}let c=await runInitSteps({dependencies:s,logger:e,options:o,parentDirectory:t,target:a});if(c.kind===`created`){e.log(`${import_picocolors.default.green(`✓`)} Created an ${EVE_WORDMARK} agent in ${import_picocolors.default.bold(c.projectPath)} ${import_picocolors.default.dim(`in ${formatElapsed(c.agentElapsedMs)}`)}`);for(let t of c.workspaceRootMutations)e.log(import_picocolors.default.yellow(`⚠ ${formatWorkspaceRootMutationWarning(t)}`))}else e.log(`${import_picocolors.default.green(`✓`)} Added an ${EVE_WORDMARK} agent to ${import_picocolors.default.bold(c.projectPath)} ${import_picocolors.default.dim(`in ${formatElapsed(c.agentElapsedMs)}`)}`),c.nodeEngineOverride!==void 0&&e.log(import_picocolors.default.yellow(`⚠ ${formatNodeEngineOverrideWarning(c.nodeEngineOverride)}`));e.log(`${import_picocolors.default.green(`✓`)} Installed dependencies ${import_picocolors.default.dim(`in ${formatElapsed(c.installElapsedMs)}`)}`),c.kind===`created`&&c.gitResult.kind===`failed`&&e.error(import_picocolors.default.yellow(`Git initialization failed: ${c.gitResult.reason}`));let l=[c.packageManager,...eveDevArguments(c.packageManager)].join(` `),d=initAgentDevHandoff({projectPath:c.projectPath,devCommand:l});if(c.agentLaunched){e.log(d);return}let f;try{f=await s.selectInitHandoff({agentName:basename(c.projectPath)})}catch(e){if(e instanceof WizardCancelledError)return;throw e}if(f!==`eve-dev`){if(e.log(import_picocolors.default.dim(`$ ${f}`)),!await s.spawnCodingAgentRepl({command:f,cwd:c.projectPath,prompt:initAgentReplPrompt({devCommand:l}),onPromptUnseeded:t=>{e.log(import_picocolors.default.yellow(`Could not seed ${f} automatically. Paste this prompt into it:`)),e.log(t)}}))throw Error(`Coding-agent REPL exited unsuccessfully in "${c.projectPath}".`);return}let p=eveDevArguments(c.packageManager);if(e.log(import_picocolors.default.dim(`$ eve dev`)),!await s.spawnPackageManager(c.packageManager,c.projectPath,p))throw Error(`Development server exited unsuccessfully in "${c.projectPath}".`)}function resolveInitEvePackageOverride(){let e=process.env[EVE_INIT_PACKAGE_SPEC_ENV]?.trim();if(!(e===void 0||e.length===0))return{nodeEngine:DEFAULT_EVE_PACKAGE_CONTRACT.nodeEngine,version:e}}export{EVE_INIT_PACKAGE_SPEC_ENV,runInitCommand};
|
|
@@ -13,8 +13,8 @@ export interface LinkCommandDependencies {
|
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* `eve link`: pick a Vercel team and project (re-linking when one is already
|
|
16
|
-
* linked), run `vercel link
|
|
17
|
-
* in `.env.local`. The flow itself is {@link runLinkFlow}, shared with the dev
|
|
16
|
+
* linked), run `vercel link` for the selected project, then pull env so the AI Gateway
|
|
17
|
+
* credential lands in `.env.local`. The flow itself is {@link runLinkFlow}, shared with the dev
|
|
18
18
|
* TUI `/model` menu's provider row. Interactive only: the pickers are the point of the command,
|
|
19
19
|
* so a non-TTY run refuses with guidance instead of guessing a project.
|
|
20
20
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NOT_AN_AGENT_MESSAGE,hasInteractiveTerminal}from"./preconditions.js";import{isEveProject}from"#setup/scaffold/index.js";import{createPrompter}from"#setup/prompter.js";import{runLinkFlow}from"#setup/flows/link.js";const defaultDependencies={isEveProject,hasInteractiveTerminal};async function runLinkCommand(t,n,r=defaultDependencies){if(!await r.isEveProject(n)){t.error(NOT_AN_AGENT_MESSAGE),process.exitCode=1;return}if(!r.hasInteractiveTerminal()){t.error("`eve link` needs an interactive terminal to pick the team and project. In CI, run `vercel link --project <name> --yes` instead."),process.exitCode=1;return}let i=r.createPrompter?.()??createPrompter();i.intro(`Link your eve agent to Vercel`);try{let e=await runLinkFlow({appRoot:n,prompter:i,deps:r.flowDeps});i.outro(e.kind===`cancelled`?`Cancelled.`:`Project linked.`)}catch(e){t.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}export{runLinkCommand};
|
|
1
|
+
import{NOT_AN_AGENT_MESSAGE,hasInteractiveTerminal}from"./preconditions.js";import{isEveProject}from"#setup/scaffold/index.js";import{createPrompter}from"#setup/prompter.js";import{runLinkFlow}from"#setup/flows/link.js";const defaultDependencies={isEveProject,hasInteractiveTerminal};async function runLinkCommand(t,n,r=defaultDependencies){if(!await r.isEveProject(n)){t.error(NOT_AN_AGENT_MESSAGE),process.exitCode=1;return}if(!r.hasInteractiveTerminal()){t.error("`eve link` needs an interactive terminal to pick the team and project. In CI, run `vercel link --project <name> --yes --non-interactive` instead."),process.exitCode=1;return}let i=r.createPrompter?.()??createPrompter();i.intro(`Link your eve agent to Vercel`);try{let e=await runLinkFlow({appRoot:n,prompter:i,deps:r.flowDeps});i.outro(e.kind===`cancelled`?`Cancelled.`:`Project linked.`)}catch(e){t.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}export{runLinkCommand};
|
|
@@ -22,6 +22,8 @@ export interface LineState {
|
|
|
22
22
|
export declare const EMPTY_LINE: LineState;
|
|
23
23
|
/** Builds a line from `text` with the caret placed at the end. */
|
|
24
24
|
export declare function lineOf(text: string): LineState;
|
|
25
|
+
/** Masks each grapheme with one bullet while preserving the caret's grapheme position. */
|
|
26
|
+
export declare function maskLine(state: LineState): LineState;
|
|
25
27
|
/** Inserts `value` at the caret and advances the caret past it. */
|
|
26
28
|
export declare function insert(state: LineState, value: string): LineState;
|
|
27
29
|
/** Deletes the grapheme before the caret (Backspace). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{inputTextWidth,offsetAtVisibleColumn}from"./terminal-text.js";import{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary}from"#shared/text-boundaries.js";const EMPTY_LINE={text:``,cursor:0};function lineOf(e){return{text:e,cursor:e.length}}function insert(e,t){if(t.length===0)return e;let r=e.text.slice(0,e.cursor)+t+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor+t.length)}}function backspace(e){if(e.cursor===0)return e;let t=previousGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,t)+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,t)}}function deleteForward(e){if(e.cursor>=e.text.length)return e;let t=nextGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,e.cursor)+e.text.slice(t);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor)}}function moveLeft(e){return e.cursor===0?e:{text:e.text,cursor:previousGraphemeBoundary(e.text,e.cursor)}}function moveRight(e){return e.cursor>=e.text.length?e:{text:e.text,cursor:nextGraphemeBoundary(e.text,e.cursor)}}function moveHome(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function moveEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function killToEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor>=t?e:{text:e.text.slice(0,e.cursor)+e.text.slice(t),cursor:e.cursor}}function killToStart(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor<=t?e:{text:e.text.slice(0,t)+e.text.slice(e.cursor),cursor:t}}function deleteWord(e){if(e.cursor===0)return e;let t=logicalLineStart(e.text,e.cursor),r=e.cursor;for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(!isWhitespace(e.text.slice(t,r)))break;r=t}for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(isWhitespace(e.text.slice(t,r)))break;r=t}let i=e.text.slice(0,r)+e.text.slice(e.cursor);return{text:i,cursor:graphemeBoundaryAtOrAfter(i,r)}}function logicalLineStart(e,t){return t===0?0:e.lastIndexOf(`
|
|
1
|
+
import{inputTextWidth,offsetAtVisibleColumn}from"./terminal-text.js";import{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary}from"#shared/text-boundaries.js";const EMPTY_LINE={text:``,cursor:0};function lineOf(e){return{text:e,cursor:e.length}}function maskLine(e){return{text:`•`.repeat(graphemes(e.text).length),cursor:graphemes(e.text.slice(0,e.cursor)).length}}function insert(e,t){if(t.length===0)return e;let r=e.text.slice(0,e.cursor)+t+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor+t.length)}}function backspace(e){if(e.cursor===0)return e;let t=previousGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,t)+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,t)}}function deleteForward(e){if(e.cursor>=e.text.length)return e;let t=nextGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,e.cursor)+e.text.slice(t);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor)}}function moveLeft(e){return e.cursor===0?e:{text:e.text,cursor:previousGraphemeBoundary(e.text,e.cursor)}}function moveRight(e){return e.cursor>=e.text.length?e:{text:e.text,cursor:nextGraphemeBoundary(e.text,e.cursor)}}function moveHome(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function moveEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function killToEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor>=t?e:{text:e.text.slice(0,e.cursor)+e.text.slice(t),cursor:e.cursor}}function killToStart(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor<=t?e:{text:e.text.slice(0,t)+e.text.slice(e.cursor),cursor:t}}function deleteWord(e){if(e.cursor===0)return e;let t=logicalLineStart(e.text,e.cursor),r=e.cursor;for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(!isWhitespace(e.text.slice(t,r)))break;r=t}for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(isWhitespace(e.text.slice(t,r)))break;r=t}let i=e.text.slice(0,r)+e.text.slice(e.cursor);return{text:i,cursor:graphemeBoundaryAtOrAfter(i,r)}}function logicalLineStart(e,t){return t===0?0:e.lastIndexOf(`
|
|
2
2
|
`,t-1)+1}function logicalLineEnd(e,t){let n=e.indexOf(`
|
|
3
3
|
`,t);return n===-1?e.length:n}function applyLineEditorKey(e,t,n){let r=n?.multiline??!1;switch(t.type){case`text`:return insert(e,r?t.value:t.value.replaceAll(`
|
|
4
4
|
`,` `));case`newline`:return r?insert(e,`
|
|
5
5
|
`):void 0;case`backspace`:return backspace(e);case`delete`:return deleteForward(e);case`left`:return moveLeft(e);case`right`:return moveRight(e);case`home`:case`ctrl-a`:return moveHome(e);case`end`:case`ctrl-e`:return moveEnd(e);case`ctrl-k`:return killToEnd(e);case`ctrl-u`:return killToStart(e);case`ctrl-w`:return deleteWord(e);default:return}}function isWhitespace(e){return/^\s+$/u.test(e)}function visibleLine(t,n,i=`…`){let a=Math.max(1,n),{text:o,cursor:s}=t,c=graphemes(o),l=c.findIndex(e=>e.start===s),u=l===-1?c.length:l,d=c[u]?.text??``;if(inputTextWidth(o)<=a)return{before:o.slice(0,s),under:d,after:o.slice(s+d.length)};let f=u,p=u+ +(d.length>0),windowWidth=(t,n)=>{let r=c[t]?.start??o.length,a=c[n]?.start??o.length;return inputTextWidth(o.slice(r,a))+(t>0?inputTextWidth(i):0)+(n<c.length?inputTextWidth(i):0)},m=!0;for(;;){let e=f>0&&windowWidth(f-1,p)<=a,t=p<c.length&&windowWidth(f,p+1)<=a;if(!e&&!t)break;m&&e||!t?--f:p+=1,m=!m}let h=c[f]?.start??o.length,g=c[p]?.start??o.length;return{before:`${f>0?i:``}${o.slice(h,s)}`,under:d,after:`${o.slice(s+d.length,g)}${p<c.length?i:``}`}}function layoutPromptInput(e){let t=[],n=0,r=0,i=0;for(let a of e.text.split(`
|
|
6
|
-
`))e.cursor>=i&&e.cursor<=i+a.length&&(n=t.length,r=e.cursor-i),t.push({text:a,start:i}),i+=a.length+1;return{rows:t,caretRow:n,caretOffset:r}}function movePromptLine(n,r){let i=layoutPromptInput(n),a=r===`up`?i.caretRow-1:i.caretRow+1;if(a<0||a>=i.rows.length)return;let o=i.rows[i.caretRow],s=i.rows[a],c=inputTextWidth(o.text.slice(0,i.caretOffset));return{text:n.text,cursor:s.start+offsetAtVisibleColumn(s.text,c)}}var PromptHistory=class{#e=[];#t=0;#n=``;add(e){if(e.trim().length!==0){if(this.#e.at(-1)===e){this.#r();return}this.#e.push(e),this.#r()}}begin(e){this.#t=this.#e.length,this.#n=e}previous(e){if(this.#e.length!==0&&(this.#t===this.#e.length&&(this.#n=e),this.#t!==0))return--this.#t,this.#e[this.#t]}next(){if(!(this.#t>=this.#e.length))return this.#t+=1,this.#t===this.#e.length?this.#n:this.#e[this.#t]}#r(){this.#t=this.#e.length,this.#n=``}};export{EMPTY_LINE,PromptHistory,applyLineEditorKey,backspace,deleteForward,deleteWord,insert,killToEnd,killToStart,layoutPromptInput,lineOf,moveEnd,moveHome,moveLeft,movePromptLine,moveRight,visibleLine};
|
|
6
|
+
`))e.cursor>=i&&e.cursor<=i+a.length&&(n=t.length,r=e.cursor-i),t.push({text:a,start:i}),i+=a.length+1;return{rows:t,caretRow:n,caretOffset:r}}function movePromptLine(n,r){let i=layoutPromptInput(n),a=r===`up`?i.caretRow-1:i.caretRow+1;if(a<0||a>=i.rows.length)return;let o=i.rows[i.caretRow],s=i.rows[a],c=inputTextWidth(o.text.slice(0,i.caretOffset));return{text:n.text,cursor:s.start+offsetAtVisibleColumn(s.text,c)}}var PromptHistory=class{#e=[];#t=0;#n=``;add(e){if(e.trim().length!==0){if(this.#e.at(-1)===e){this.#r();return}this.#e.push(e),this.#r()}}begin(e){this.#t=this.#e.length,this.#n=e}previous(e){if(this.#e.length!==0&&(this.#t===this.#e.length&&(this.#n=e),this.#t!==0))return--this.#t,this.#e[this.#t]}next(){if(!(this.#t>=this.#e.length))return this.#t+=1,this.#t===this.#e.length?this.#n:this.#e[this.#t]}#r(){this.#t=this.#e.length,this.#n=``}};export{EMPTY_LINE,PromptHistory,applyLineEditorKey,backspace,deleteForward,deleteWord,insert,killToEnd,killToStart,layoutPromptInput,lineOf,maskLine,moveEnd,moveHome,moveLeft,movePromptLine,moveRight,visibleLine};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{toErrorMessage}from"#shared/errors.js";function createPromptCommandHandler(t){return{async handle(n,r){let i=t.appRoot;if(i===void 0)return{message:`/${n.name} needs eve dev running the local server (it is not available with --url).`};if(n.name===`model`&&n.argument.length>0)try{let{changeAgentModel:e,formatApplyModelOutcome:r,modelChangeRefusalForUneditableModel:a}=await import(`#setup/flows/model.js`),o=await(t.modelChangeRefusal??a)(i);return o===null?{message:r(await(t.applyModel??e)({appRoot:i,slug:n.argument}))}:{message:o}}catch(t){return{message:`Couldn't change the model: ${toErrorMessage(t)}`}}let a=r.renderer.setupFlow;if(a===void 0)return{message:`/${n.name} is not supported by this renderer.`};let o;try{o=await import(`./setup-commands.js`)}catch(t){return{message:`/${n.name} failed: ${toErrorMessage(t)}`}}let{runTuiSetupCommand:s,
|
|
1
|
+
import{toErrorMessage}from"#shared/errors.js";function createPromptCommandHandler(t){return{async handle(n,r){let i=t.appRoot;if(i===void 0)return{message:`/${n.name} needs eve dev running the local server (it is not available with --url).`};if(n.name===`model`&&n.argument.length>0)try{let{changeAgentModel:e,formatApplyModelOutcome:r,modelChangeRefusalForUneditableModel:a}=await import(`#setup/flows/model.js`),o=await(t.modelChangeRefusal??a)(i);return o===null?{message:r(await(t.applyModel??e)({appRoot:i,slug:n.argument}))}:{message:o}}catch(t){return{message:`Couldn't change the model: ${toErrorMessage(t)}`}}let a=r.renderer.setupFlow;if(a===void 0)return{message:`/${n.name} is not supported by this renderer.`};let o;try{o=await import(`./setup-commands.js`)}catch(t){return{message:`/${n.name} failed: ${toErrorMessage(t)}`}}let{runTuiSetupCommand:s,SETUP_FLOW_CONFIG:c}=o,l=c[n.name];a.begin(l.title,l.indicator);let u=!0;try{let e={command:n.name,appRoot:i,renderer:a};r.initialModelStep!==void 0&&(e.initialModelStep=r.initialModelStep),t.flows!==void 0&&(e.flows=t.flows);let o=await s(e);u=o.preserveFlowDiagnostics;let c={message:o.message};return o.effect!==void 0&&(c.effect=o.effect),c}finally{a.end({preserveDiagnostics:u})}}}}export{createPromptCommandHandler};
|
|
@@ -10,13 +10,32 @@ import type { SetupFlowRenderer } from "./setup-flow.js";
|
|
|
10
10
|
import type { VercelStatusEffect } from "./vercel-status.js";
|
|
11
11
|
export type TuiSetupCommand = PromptCommandExtensionName;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* prompts), and without a constant header those later questions float
|
|
17
|
-
* unanchored in the panel.
|
|
13
|
+
* Panel title and loading indicator per command. The bordered panel never
|
|
14
|
+
* repeats the echoed command verbatim, but it keeps a constant title as flows
|
|
15
|
+
* move past their opening question.
|
|
18
16
|
*/
|
|
19
|
-
export declare const
|
|
17
|
+
export declare const SETUP_FLOW_CONFIG: {
|
|
18
|
+
vc: {
|
|
19
|
+
title: string;
|
|
20
|
+
indicator: "spinner";
|
|
21
|
+
};
|
|
22
|
+
login: {
|
|
23
|
+
title: string;
|
|
24
|
+
indicator: "spinner";
|
|
25
|
+
};
|
|
26
|
+
model: {
|
|
27
|
+
title: string;
|
|
28
|
+
indicator: "pulse";
|
|
29
|
+
};
|
|
30
|
+
channels: {
|
|
31
|
+
title: string;
|
|
32
|
+
indicator: "pulse";
|
|
33
|
+
};
|
|
34
|
+
deploy: {
|
|
35
|
+
title: string;
|
|
36
|
+
indicator: "spinner";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
20
39
|
/** The prompter surface plus the working-state interrupt trap a command races against. */
|
|
21
40
|
export type TuiSetupCommandRenderer = TuiPrompterRenderer & Pick<SetupFlowRenderer, "waitForInterrupt">;
|
|
22
41
|
export interface TuiSetupCommandInput {
|
|
@@ -53,7 +72,7 @@ export interface TuiSetupCommandResult {
|
|
|
53
72
|
* Runs one TUI setup command (/model, /channels, /deploy) over the
|
|
54
73
|
* shared setup flows, asking through the TUI's own bordered panel. Never throws:
|
|
55
74
|
* every outcome — done, cancelled, failed — folds into the returned command
|
|
56
|
-
* result. Ctrl-C or Esc on the working
|
|
75
|
+
* result. Ctrl-C or Esc on the working indicator (no question open) aborts the
|
|
57
76
|
* active flow, then keeps command ownership until its subprocesses and setup
|
|
58
77
|
* stack have unwound.
|
|
59
78
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createTuiPrompter}from"./tui-prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{runChannelsFlow}from"#setup/flows/channels.js";import{runInstallVercelCliFlow}from"#setup/flows/install-vercel-cli.js";import{runLoginFlow}from"#setup/flows/login.js";import{runModelFlow}from"#setup/flows/model.js";import{openUrl}from"#setup/primitives/open-url.js";import{slackMessageDeepLink}from"#setup/slack-connect.js";
|
|
2
|
-
`),preserveFlowDiagnostics:!1};return
|
|
1
|
+
import{createTuiPrompter}from"./tui-prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{runChannelsFlow}from"#setup/flows/channels.js";import{runInstallVercelCliFlow}from"#setup/flows/install-vercel-cli.js";import{runLoginFlow}from"#setup/flows/login.js";import{runModelFlow}from"#setup/flows/model.js";import{openUrl}from"#setup/primitives/open-url.js";import{slackMessageDeepLink}from"#setup/slack-connect.js";const SETUP_FLOW_CONFIG={vc:{title:`Install the Vercel CLI`,indicator:`spinner`},login:{title:`Log in to Vercel`,indicator:`spinner`},model:{title:`Configure the agent model`,indicator:`pulse`},channels:{title:`Agent channels`,indicator:`pulse`},deploy:{title:`Deploy to Vercel`,indicator:`spinner`}};function muteableRenderer(e,t){return{readSelect:n=>t()?Promise.resolve(void 0):e.readSelect(n),readEditableSelect:n=>t()?Promise.resolve(void 0):e.readEditableSelect(n),readText:n=>t()?Promise.resolve(void 0):e.readText(n),readAcknowledge:n=>t()?Promise.resolve():e.readAcknowledge(n),readChoice:n=>t()?{choice:Promise.resolve(void 0),close:()=>{}}:e.readChoice(n),setStatus:n=>{t()||e.setStatus(n)},renderLine:(n,r)=>{(!t()||r===`warning`||r===`error`)&&e.renderLine(n,r)},renderOutput:n=>{t()||e.renderOutput(n)}}}async function runTuiSetupCommand(t){let{command:r}=t,i=!1,a=new AbortController,o=(t.createPrompter??createTuiPrompter)(muteableRenderer(t.renderer,()=>i)),s=t.renderer.waitForInterrupt(),c=Symbol(`interrupted`),l=executeSetupCommand(t,o,a.signal);try{let e=await Promise.race([l,s.promise.then(()=>c)]);return e===c?(i=!0,a.abort(new WizardCancelledError),{...await l,message:`/${r} interrupted.`,preserveFlowDiagnostics:!0}):e}finally{s.dispose(),t.renderer.setStatus(void 0)}}async function executeSetupCommand(e,r,c){let{command:l,appRoot:u}=e,d={runInstallVercelCliFlow,runLoginFlow,runModelFlow,runChannelsFlow,runDeployFlow,...e.flows};try{switch(l){case`vc`:return installVercelCliResultMessage(await d.runInstallVercelCliFlow({appRoot:u,prompter:r,signal:c}));case`login`:return loginResultMessage(await d.runLoginFlow({appRoot:u,prompter:r,signal:c}));case`model`:{let t={appRoot:u,prompter:r,signal:c};e.initialModelStep!==void 0&&(t.initialStep=e.initialModelStep);let n=await d.runModelFlow(t);if(n.kind===`cancelled`)return{message:`/model cancelled.`,preserveFlowDiagnostics:!1};let i=[];n.modelMessage!==void 0&&i.push(n.modelMessage),n.providerOutcome!==void 0&&i.push(providerOutcomeMessage(n.providerOutcome));let a={message:i.join(`
|
|
2
|
+
`),preserveFlowDiagnostics:!1};return n.providerOutcome!==void 0&&(a.effect={kind:`model-access-changed`}),a}case`channels`:{let e=await d.runChannelsFlow({appRoot:u,prompter:r,signal:c});switch(e.kind){case`failed`:return pendingChannelsResult(`Channel files changed, but /channels failed: ${e.message}`);case`cancelled`:return{message:`/channels cancelled.`,preserveFlowDiagnostics:!0};case`deploy-and-chat`:return await runDeployAndChat(d,{appRoot:u,prompter:r,signal:c},e.chat);case`done`:return e.addedChannels.length===0?{message:`No channels added.`,preserveFlowDiagnostics:!0}:{message:`Channels added: ${e.addedChannels.join(`, `)} — run /deploy to ship them.`,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}}case`deploy`:{let e=await d.runDeployFlow({appRoot:u,prompter:r,interactive:!0,signal:c});return e.kind===`cancelled`?{message:`/deploy cancelled.`,preserveFlowDiagnostics:!0}:e.kind===`needs-link`?{message:`Not linked to a Vercel project — run /model to connect one first.`,preserveFlowDiagnostics:!0}:{message:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}}}catch(e){if(e instanceof WizardCancelledError)return{message:`/${l} cancelled.`,preserveFlowDiagnostics:l!==`model`};let t=vercelActionOutcome(e,l);return t===void 0?{message:`/${l} failed: ${e instanceof Error?e.message:String(e)}`,preserveFlowDiagnostics:!0}:t}}function vercelActionOutcome(e,t){if(!(e instanceof HumanActionRequiredError))return;let n=vercelActionMessage(e.action.kind,t);return n===void 0?void 0:{message:n,preserveFlowDiagnostics:!0}}function vercelActionMessage(e,t){switch(e){case`vercel-login`:return`You're not logged in to Vercel — run /login, then retry /${t}.`;case`vercel-forbidden`:return`Vercel denied access to that team — run /login to re-authenticate (for example to complete SSO), or pick a team you can access, then retry /${t}.`;case`vercel-cli-missing`:return`The Vercel CLI isn't installed — run /vc to install it, then retry /${t}.`;default:return}}async function runDeployAndChat(e,t,r){let i;try{i=await e.runDeployFlow({...t,interactive:!0})}catch(e){if(e instanceof WizardCancelledError)return pendingChannelsResult(`Channels added, but /deploy was cancelled. Run /deploy to ship them.`);let t=vercelActionOutcome(e,`deploy`);return pendingChannelsResult(t===void 0?`Channels added, but /deploy failed: ${e instanceof Error?e.message:String(e)}`:`Channels added. ${t.message}`)}if(i.kind===`cancelled`)return pendingChannelsResult(`Channels added, but /deploy was cancelled. Run /deploy to ship them.`);if(i.kind===`needs-link`)return pendingChannelsResult(`Channels added, but this directory is not linked to Vercel. Run /model, then /deploy.`);let a=i.productionUrl===void 0?`Deployed.`:`Deployed: ${i.productionUrl}`,o;if(r.chatUrl===void 0)o=`Message your agent in Slack to see it live.`;else{let e=slackMessageDeepLink(r.chatUrl);openUrl(e),o=`Chat with your agent in Slack: ${e}`}return{message:`${a}\n${o}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}function installVercelCliResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/vc cancelled.`,preserveFlowDiagnostics:!1};case`already`:return{message:`The Vercel CLI is already installed.`,preserveFlowDiagnostics:!1};case`failed`:return{message:"Couldn't install the Vercel CLI — install it manually with `npm i -g vercel@latest`.",preserveFlowDiagnostics:!0};case`installed`:return{message:`Installed the Vercel CLI. Run /login next.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}}}}function loginResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/login cancelled.`,preserveFlowDiagnostics:!1};case`already`:return{message:`You're already logged in to Vercel.`,preserveFlowDiagnostics:!1};case`cli-missing`:return{message:`The Vercel CLI isn't installed — run /vc to install it, then retry /login.`,preserveFlowDiagnostics:!0};case`failed`:return{message:`Vercel login didn't complete — run /login to try again.`,preserveFlowDiagnostics:!0};case`logged-in`:return{message:`Logged in to Vercel.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}};case`unavailable`:return{message:`Couldn't reach Vercel — check your connection, then retry /login.`,preserveFlowDiagnostics:!0}}}function pendingChannelsResult(e){return{message:e,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}function providerOutcomeMessage(e){let{credential:t,status:n}=e;return n.kind===`gateway-project`?t===void 0?`Project linked. No model credential found; set AI_GATEWAY_API_KEY in .env.local.`:`Project linked. Connected to AI Gateway via ${t}.`:n.kind===`gateway-key`?`Connected to AI Gateway via ${n.envKey} in ${n.envFile}.`:`Provider updated — no gateway credential detected; set AI_GATEWAY_API_KEY in .env.local.`}export{SETUP_FLOW_CONFIG,runTuiSetupCommand};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChannelSetupChoice, ChannelSetupChoiceOptions } from "#setup/cli/index.js";
|
|
2
|
+
import type { SearchActionOption } from "#setup/cli/select-state.js";
|
|
2
3
|
import type { SelectNotice } from "#setup/prompter.js";
|
|
3
4
|
import type { SetupPanelOption } from "./setup-panel.js";
|
|
4
5
|
export type SetupEditableSelectResult = {
|
|
@@ -9,6 +10,8 @@ export type SetupEditableSelectResult = {
|
|
|
9
10
|
value: string;
|
|
10
11
|
text: string;
|
|
11
12
|
};
|
|
13
|
+
/** Animation shown while a setup flow is between questions. */
|
|
14
|
+
export type SetupFlowIndicator = "spinner" | "pulse";
|
|
12
15
|
interface SetupSelectRequestBase {
|
|
13
16
|
message: string;
|
|
14
17
|
options: readonly SetupPanelOption[];
|
|
@@ -18,10 +21,14 @@ interface SetupSingleSelectRequest extends SetupSelectRequestBase {
|
|
|
18
21
|
kind: "single" | "stacked" | "task-list";
|
|
19
22
|
initialValue?: string;
|
|
20
23
|
}
|
|
24
|
+
interface SetupSearchAction extends SearchActionOption {
|
|
25
|
+
load?(query: string): Promise<readonly SetupPanelOption[]>;
|
|
26
|
+
}
|
|
21
27
|
interface SetupSearchSelectRequest extends SetupSelectRequestBase {
|
|
22
28
|
kind: "search";
|
|
23
29
|
initialValue?: string;
|
|
24
30
|
placeholder?: string;
|
|
31
|
+
searchAction?: SetupSearchAction;
|
|
25
32
|
}
|
|
26
33
|
interface SetupMultiSelectRequest extends SetupSelectRequestBase {
|
|
27
34
|
kind: "multi";
|
|
@@ -41,7 +48,7 @@ interface SetupSearchableMultiSelectRequest extends SetupSelectRequestBase {
|
|
|
41
48
|
*/
|
|
42
49
|
export type SetupSelectRequest = SetupSingleSelectRequest | SetupSearchSelectRequest | SetupMultiSelectRequest | SetupSearchableMultiSelectRequest;
|
|
43
50
|
export interface SetupFlowRenderer {
|
|
44
|
-
begin(title: string): void;
|
|
51
|
+
begin(title: string, indicator?: SetupFlowIndicator): void;
|
|
45
52
|
end(options?: {
|
|
46
53
|
preserveDiagnostics?: boolean;
|
|
47
54
|
}): void;
|
|
@@ -71,7 +78,7 @@ export interface SetupFlowRenderer {
|
|
|
71
78
|
}): Promise<void>;
|
|
72
79
|
/**
|
|
73
80
|
* Presents an inert context row and a separate action menu beside the live
|
|
74
|
-
* flow
|
|
81
|
+
* flow indicator. Returns the choice plus a `close()` that dismisses the menu
|
|
75
82
|
* when a concurrent wait resolves first. Used by the Slack install wait for
|
|
76
83
|
* "Try again" / "Cancel": the poll keeps running while the prompt is up, and
|
|
77
84
|
* whichever settles first wins.
|
|
@@ -81,7 +88,7 @@ export interface SetupFlowRenderer {
|
|
|
81
88
|
renderLine(text: string, tone: "info" | "success" | "warning" | "error"): void;
|
|
82
89
|
renderOutput(text: string): void;
|
|
83
90
|
/**
|
|
84
|
-
* Arms a key trap for the flow's working state — the status
|
|
91
|
+
* Arms a key trap for the flow's working state — the status indicator between
|
|
85
92
|
* questions, where no prompt is consuming keys. Ctrl-C or Esc resolves the
|
|
86
93
|
* promise so the command can abandon an in-flight flow (e.g. a parked
|
|
87
94
|
* `vercel connect create` browser OAuth). Open questions own their keys; the
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* column 3.
|
|
15
15
|
*/
|
|
16
16
|
import type { ChannelSetupAction, PromptOption } from "#setup/cli/index.js";
|
|
17
|
-
import { type SelectState } from "#setup/cli/select-state.js";
|
|
17
|
+
import { type SearchActionOption, type SelectState } from "#setup/cli/select-state.js";
|
|
18
18
|
import type { SelectNotice } from "#setup/prompter.js";
|
|
19
19
|
import { type LineState } from "./line-editor.js";
|
|
20
20
|
import type { Theme } from "./theme.js";
|
|
@@ -28,7 +28,10 @@ interface SetupQuestionPanelBase {
|
|
|
28
28
|
}
|
|
29
29
|
interface SetupSelectPanelBase extends SetupQuestionPanelBase {
|
|
30
30
|
options: readonly SetupPanelOption[];
|
|
31
|
+
searchAction?: SearchActionOption;
|
|
31
32
|
select: SelectState;
|
|
33
|
+
/** Live frame rendered beside a searchable input while it loads replacement rows. */
|
|
34
|
+
loadingFrame?: string;
|
|
32
35
|
}
|
|
33
36
|
interface SetupEditableRow {
|
|
34
37
|
/**
|
|
@@ -98,29 +101,34 @@ export interface FlowPanelLine {
|
|
|
98
101
|
*/
|
|
99
102
|
evidence?: boolean;
|
|
100
103
|
}
|
|
104
|
+
/** One already-resolved animation frame and its active color. */
|
|
105
|
+
export interface FlowPanelIndicator {
|
|
106
|
+
glyph: string;
|
|
107
|
+
color: "green" | "yellow";
|
|
108
|
+
}
|
|
101
109
|
export type FlowPanelContent = {
|
|
102
110
|
kind: "question";
|
|
103
111
|
rows: readonly string[];
|
|
104
|
-
/** The install wait keeps its
|
|
112
|
+
/** The install wait keeps its indicator above the concurrent actions. */
|
|
105
113
|
status?: {
|
|
106
114
|
text: string;
|
|
107
|
-
|
|
115
|
+
indicator: FlowPanelIndicator;
|
|
108
116
|
};
|
|
109
117
|
} | {
|
|
110
118
|
kind: "status";
|
|
111
119
|
status: {
|
|
112
120
|
text: string;
|
|
113
|
-
|
|
121
|
+
indicator: FlowPanelIndicator;
|
|
114
122
|
};
|
|
115
123
|
/** Latest child-process output shown transiently beneath the status. */
|
|
116
124
|
preview?: string;
|
|
117
125
|
} | {
|
|
118
126
|
kind: "preview";
|
|
119
127
|
text: string;
|
|
120
|
-
|
|
128
|
+
indicator: FlowPanelIndicator;
|
|
121
129
|
} | {
|
|
122
130
|
kind: "idle";
|
|
123
|
-
|
|
131
|
+
indicator: FlowPanelIndicator;
|
|
124
132
|
};
|
|
125
133
|
/** The whole bordered section: title, recent progress, and one explicit mode. */
|
|
126
134
|
export interface FlowPanelState {
|
|
@@ -131,7 +139,7 @@ export interface FlowPanelState {
|
|
|
131
139
|
}
|
|
132
140
|
/**
|
|
133
141
|
* Paints the bordered flow panel. Everything a running command produces lives
|
|
134
|
-
* here — progress, questions, the status
|
|
142
|
+
* here — progress, questions, the status indicator — and the panel vanishes
|
|
135
143
|
* wholesale when the command resolves; only the command echo and the elbow
|
|
136
144
|
* outcome persist in the transcript.
|
|
137
145
|
*/
|
|
@@ -143,7 +151,7 @@ export declare function renderFlowPanel(state: FlowPanelState, theme: Theme, wid
|
|
|
143
151
|
* the cursor and advertises the rest with a count footer.
|
|
144
152
|
*/
|
|
145
153
|
export declare function renderSelectQuestion(state: SetupSelectPanelState, theme: Theme, width: number): string[];
|
|
146
|
-
/** Paints a text question section: message, a
|
|
154
|
+
/** Paints a text question section: message, a block-cursor input line, hints. */
|
|
147
155
|
export declare function renderTextQuestion(state: SetupTextPanelState, theme: Theme, width: number, caretVisible: boolean): string[];
|
|
148
156
|
/**
|
|
149
157
|
* Paints a static acknowledgement section (for the flow panel): a heading and
|