From 6fa521f0f37b2076d8c5e7252a441bd02d3421da Mon Sep 17 00:00:00 2001 From: owenkephart Date: Wed, 29 Jul 2026 21:41:51 +0000 Subject: [PATCH] fix(eve): prompt for channel credentials --- .../src/cli/dev/tui/terminal-renderer.test.ts | 22 +++++++++++++++++++ .../eve/src/cli/dev/tui/terminal-renderer.ts | 7 ++++++ .../integrations/channels/environment.test.ts | 4 ++-- .../integrations/channels/environment.ts | 6 ++--- .../src/setup/integrations/channels/slack.ts | 6 ++++- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/eve/src/cli/dev/tui/terminal-renderer.test.ts b/packages/eve/src/cli/dev/tui/terminal-renderer.test.ts index c37df078a..4bbfdb418 100644 --- a/packages/eve/src/cli/dev/tui/terminal-renderer.test.ts +++ b/packages/eve/src/cli/dev/tui/terminal-renderer.test.ts @@ -3035,6 +3035,28 @@ describe("TerminalRenderer (inline scrollback)", () => { expect(screen.snapshot()).toContain("tui/setup-panel.ts changed · rebuilt"); }); + it("updates the rebuild status before the watcher writes a newline", () => { + const screen = new MockScreen({ columns: 100, rows: 30 }); + const input = new MockUserInput(); + const renderer = new TerminalRenderer({ + input, + output: screen, + captureForeignOutput: true, + logs: "all", + unicode: true, + }); + renderer.renderAgentHeader({ name: "Weather Agent", serverUrl: "http://localhost:3000" }); + + process.stdout.write( + formatChangeDetectedLogLine("/app", [{ event: "change", path: "/app/package.json" }]), + ); + + const snapshot = screen.snapshot(); + expect(snapshot).toContain("package.json changed · rebuilding…"); + expect(snapshot).not.toContain("[eve:dev] change detected"); + renderer.shutdown(); + }); + it("flips the status row to reloading on a structural change", () => { const screen = new MockScreen({ columns: 100, rows: 30 }); const input = new MockUserInput(); diff --git a/packages/eve/src/cli/dev/tui/terminal-renderer.ts b/packages/eve/src/cli/dev/tui/terminal-renderer.ts index 78b14e6b1..1ba275e21 100644 --- a/packages/eve/src/cli/dev/tui/terminal-renderer.ts +++ b/packages/eve/src/cli/dev/tui/terminal-renderer.ts @@ -4121,6 +4121,13 @@ export class TerminalRenderer implements AgentTUIRenderer { #handleForeignOutput(source: "stdout" | "stderr", text: string): void { const combined = (source === "stdout" ? this.#stdoutLogBuffer : this.#stderrLogBuffer) + text; + if (source === "stdout" && parseDevRebuildLogLine(combined.trimEnd()) !== undefined) { + this.#stdoutLogBuffer = ""; + this.#diagnostics?.append({ source, detail: combined.trimEnd() }); + this.#handleCapturedStdout(combined.trimEnd()); + this.#paint(); + return; + } const lastNewline = combined.lastIndexOf("\n"); const remainder = lastNewline === -1 ? combined : combined.slice(lastNewline + 1); diff --git a/packages/eve/src/setup/integrations/channels/environment.test.ts b/packages/eve/src/setup/integrations/channels/environment.test.ts index 58a0f5912..77ea05bbd 100644 --- a/packages/eve/src/setup/integrations/channels/environment.test.ts +++ b/packages/eve/src/setup/integrations/channels/environment.test.ts @@ -13,11 +13,11 @@ describe("channel setup environment", () => { ); }); - it("reports the portable fallback when logged out", () => { + it("reports the credential choice when logged out", () => { const environment = channelSetupEnvironment("logged-out", { kind: "unresolved" }); expect(environment).toEqual({ vercel: { kind: "unavailable", reason: "logged-out" } }); expect(describeChannelSetupEnvironment(environment)).toBe( - "No authenticated Vercel account found; using portable channel setup.", + "No authenticated Vercel account found; choose Vercel Connect or portable credentials.", ); }); }); diff --git a/packages/eve/src/setup/integrations/channels/environment.ts b/packages/eve/src/setup/integrations/channels/environment.ts index 536a044cc..3f887302c 100644 --- a/packages/eve/src/setup/integrations/channels/environment.ts +++ b/packages/eve/src/setup/integrations/channels/environment.ts @@ -22,11 +22,11 @@ export function describeChannelSetupEnvironment(environment: ChannelSetupEnviron } switch (environment.vercel.reason) { case "logged-out": - return "No authenticated Vercel account found; using portable channel setup."; + return "No authenticated Vercel account found; choose Vercel Connect or portable credentials."; case "cli-missing": - return "Vercel CLI not found; using portable channel setup."; + return "Vercel CLI not found; choose Vercel Connect or portable credentials."; case "unavailable": - return "Could not verify the Vercel account; using portable channel setup."; + return "Could not verify the Vercel account; choose Vercel Connect or portable credentials."; } } diff --git a/packages/eve/src/setup/integrations/channels/slack.ts b/packages/eve/src/setup/integrations/channels/slack.ts index ae3f33c9a..564d73036 100644 --- a/packages/eve/src/setup/integrations/channels/slack.ts +++ b/packages/eve/src/setup/integrations/channels/slack.ts @@ -5,7 +5,6 @@ import { WizardCancelledError } from "../../step.js"; async function choosePortableCredentials( context: Parameters[0], ): Promise<"vercel-connect" | "environment" | "cancelled"> { - if (context.environment.vercel.kind === "available") return "vercel-connect"; if (context.presetPortableCredentials !== undefined) { return context.presetPortableCredentials ? "environment" : "vercel-connect"; } @@ -41,6 +40,11 @@ export const SLACK_CHANNEL_SETUP: ChannelSetupIntegration = { async setup(context) { const credentials = await choosePortableCredentials(context); if (credentials === "cancelled") return { kind: "cancelled" }; + if (credentials === "vercel-connect" && context.environment.vercel.kind === "unavailable") { + throw new Error( + "Vercel Connect requires an authenticated Vercel CLI. Run `vercel login`, then retry Slack setup.", + ); + } const result = await runChannelSetup( context,