eve 0.13.5 → 0.13.6

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/src/channel/send.d.ts +3 -1
  3. package/dist/src/channel/send.js +1 -1
  4. package/dist/src/channel/types.d.ts +6 -0
  5. package/dist/src/chunks/{use-eve-agent-_dbX0ASK.js → use-eve-agent-8X2UMr8q.js} +26 -2
  6. package/dist/src/chunks/{use-eve-agent-BjAM8_a3.js → use-eve-agent-9ZNiSFMb.js} +26 -2
  7. package/dist/src/cli/dev/tui/blocks.d.ts +1 -1
  8. package/dist/src/cli/dev/tui/blocks.js +1 -1
  9. package/dist/src/cli/dev/tui/prompt-command-handler.d.ts +5 -1
  10. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  11. package/dist/src/cli/dev/tui/prompt-commands.d.ts +7 -7
  12. package/dist/src/cli/dev/tui/prompt-commands.js +2 -2
  13. package/dist/src/cli/dev/tui/remote-auth-command.d.ts +11 -0
  14. package/dist/src/cli/dev/tui/remote-auth-command.js +3 -0
  15. package/dist/src/cli/dev/tui/remote-auth-result.d.ts +5 -0
  16. package/dist/src/cli/dev/tui/remote-auth-result.js +2 -1
  17. package/dist/src/cli/dev/tui/remote-auth.d.ts +30 -0
  18. package/dist/src/cli/dev/tui/remote-auth.js +1 -0
  19. package/dist/src/cli/dev/tui/remote-connection-probe.d.ts +3 -1
  20. package/dist/src/cli/dev/tui/remote-connection-probe.js +1 -1
  21. package/dist/src/cli/dev/tui/remote-connection-types.d.ts +2 -1
  22. package/dist/src/cli/dev/tui/remote-connection.js +1 -1
  23. package/dist/src/cli/dev/tui/runner.d.ts +18 -1
  24. package/dist/src/cli/dev/tui/runner.js +1 -1
  25. package/dist/src/cli/dev/tui/setup-commands.d.ts +4 -4
  26. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  27. package/dist/src/cli/dev/tui/setup-issues.d.ts +1 -1
  28. package/dist/src/cli/dev/tui/setup-issues.js +1 -1
  29. package/dist/src/cli/dev/tui/status-line.d.ts +6 -14
  30. package/dist/src/cli/dev/tui/status-line.js +1 -1
  31. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +8 -1
  32. package/dist/src/cli/dev/tui/terminal-renderer.js +9 -9
  33. package/dist/src/cli/dev/tui/test/index.d.ts +1 -0
  34. package/dist/src/cli/dev/tui/test/index.js +1 -1
  35. package/dist/src/cli/dev/tui/tui.d.ts +1 -1
  36. package/dist/src/cli/dev/tui/tui.js +1 -1
  37. package/dist/src/cli/dev/tui/vercel-trusted-sources.js +1 -1
  38. package/dist/src/cli/run.js +1 -1
  39. package/dist/src/client/agent-info-error.d.ts +17 -0
  40. package/dist/src/client/agent-info-error.js +1 -0
  41. package/dist/src/client/client.d.ts +4 -0
  42. package/dist/src/client/client.js +1 -1
  43. package/dist/src/client/index.d.ts +1 -0
  44. package/dist/src/client/index.js +1 -1
  45. package/dist/src/client/ndjson.js +3 -3
  46. package/dist/src/context/keys.d.ts +1 -0
  47. package/dist/src/context/keys.js +1 -1
  48. package/dist/src/evals/cli/eval-client.js +1 -1
  49. package/dist/src/execution/delegated-parent-notification.js +1 -1
  50. package/dist/src/execution/durable-session-store.js +1 -1
  51. package/dist/src/execution/eve-workflow-attributes.d.ts +8 -0
  52. package/dist/src/execution/eve-workflow-attributes.js +1 -1
  53. package/dist/src/execution/ndjson-stream.d.ts +19 -0
  54. package/dist/src/execution/ndjson-stream.js +3 -0
  55. package/dist/src/execution/runtime-context.js +1 -1
  56. package/dist/src/execution/subagent-adapter.js +1 -1
  57. package/dist/src/execution/turn-workflow.js +1 -1
  58. package/dist/src/execution/workflow-entry.js +1 -1
  59. package/dist/src/execution/workflow-runtime.d.ts +1 -1
  60. package/dist/src/execution/workflow-runtime.js +1 -3
  61. package/dist/src/execution/workflow-steps.js +1 -1
  62. package/dist/src/internal/application/package.js +1 -1
  63. package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
  64. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  65. package/dist/src/internal/workflow/configure-world.js +1 -1
  66. package/dist/src/internal/workflow/queue-namespace.d.ts +0 -1
  67. package/dist/src/internal/workflow/queue-namespace.js +1 -1
  68. package/dist/src/internal/workflow/runtime.d.ts +4 -0
  69. package/dist/src/internal/workflow/runtime.js +1 -0
  70. package/dist/src/internal/workflow-bundle/builder-support.js +6 -6
  71. package/dist/src/internal/workflow-bundle/builder.js +2 -2
  72. package/dist/src/internal/workflow-bundle/vercel-workflow-output.js +1 -1
  73. package/dist/src/public/channels/github/dispatch.d.ts +22 -1
  74. package/dist/src/public/channels/github/dispatch.js +1 -1
  75. package/dist/src/public/channels/github/githubChannel.d.ts +21 -3
  76. package/dist/src/public/channels/github/githubChannel.js +1 -1
  77. package/dist/src/public/channels/github/inbound.d.ts +46 -1
  78. package/dist/src/public/channels/github/inbound.js +2 -2
  79. package/dist/src/public/channels/github/index.d.ts +1 -1
  80. package/dist/src/public/channels/github/state.d.ts +3 -1
  81. package/dist/src/public/channels/github/state.js +1 -1
  82. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  83. package/dist/src/runtime/connections/callback-route.js +1 -1
  84. package/dist/src/runtime/session-callback-route.js +1 -1
  85. package/dist/src/services/dev-client/request-headers.d.ts +2 -0
  86. package/dist/src/services/dev-client/request-headers.js +1 -1
  87. package/dist/src/services/dev-client/vercel-auth-error.d.ts +12 -14
  88. package/dist/src/services/dev-client/vercel-auth-error.js +9 -2
  89. package/dist/src/setup/flows/install-vercel-cli.d.ts +1 -1
  90. package/dist/src/setup/flows/login.d.ts +3 -1
  91. package/dist/src/setup/flows/login.js +1 -1
  92. package/dist/src/setup/scaffold/create/project.js +1 -1
  93. package/dist/src/setup/vercel-deployment.d.ts +12 -5
  94. package/dist/src/setup/vercel-deployment.js +1 -1
  95. package/dist/src/setup/vercel-project-api.d.ts +13 -2
  96. package/dist/src/setup/vercel-project-api.js +1 -1
  97. package/dist/src/setup/vercel-project.d.ts +5 -1
  98. package/dist/src/setup/vercel-project.js +0 -0
  99. package/dist/src/setup/verified-remote-client.js +1 -1
  100. package/dist/src/svelte/index.js +1 -1
  101. package/dist/src/svelte/use-eve-agent.js +1 -1
  102. package/dist/src/vue/index.js +1 -1
  103. package/dist/src/vue/use-eve-agent.js +1 -1
  104. package/docs/channels/github.mdx +13 -1
  105. package/docs/channels/slack.mdx +1 -1
  106. package/docs/guides/dev-tui.md +22 -12
  107. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # eve
2
2
 
3
+ ## 0.13.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 7f66a06: Add opt-in GitHub channel hooks for check suite, check run, and workflow run webhooks, with normalized CI metadata and pull request dispatch.
8
+ - a63dfa2: Project search now resolves exact names directly and ranks one fallback result page, avoiding unbounded substring-match pagination for short queries.
9
+ - a63dfa2: Fixed remote `/vc:login` rejecting a freshly resolved Vercel project with "The local Vercel OIDC token does not match the resolved deployment: owner_id." The verified deployment now takes its owner id from Vercel's response instead of the team slug used to scope the lookup, so it matches the OIDC token's `owner_id` claim.
10
+ - a63dfa2: In remote sessions, `/vc:login` resolves the target Vercel project and owning team from the deployment URL. When the target requires authentication and Vercel cannot resolve its host in the active scope, the flow asks you to select another team, then reruns the lookup in that scope. When access is denied, for example because a team SSO session expired, it re-authenticates and retries.
11
+ - a63dfa2: Remote `eve dev --url` now treats `/eve/v1/info` as best-effort inspection rather than a connection gate. Once authentication succeeds and the deployment is reachable, the session connects even when the agent info route is absent (confirmed via the public health route) or returns an unrecognized shape (e.g. a deployment built from an older eve). Inspection-only data is simply omitted from the header in that case, and the underlying parse failure now names the offending fields instead of an opaque message.
12
+ - a63dfa2: Remote `eve dev --url` sessions now show deployment and authentication state, try refreshed project-scoped OIDC credentials at startup, and open a cancellable `/vc:login` recovery flow when access is rejected. The flow can update the target project's Trusted Sources after confirmation.
13
+ - c5071e6: Ensure every eve Workflow runtime entrypoint installs the eve queue namespace through a single guarded boundary.
14
+ - c9e895b: Fix `eve dev` streaming throughput and time-to-first-token degrading as parked (`ask_question` / HITL) sessions accumulate. The dev runtime's NDJSON event-stream reader now forwards cancellation to the underlying run stream, so disconnecting from a parked session no longer leaks a filesystem polling loop for the life of the dev server.
15
+ - c6b2da8: Add `$eve.channel_request_id` workflow attributes from Vercel's `x-vercel-id` header so session and turn workflow runs can be joined back to the inbound request that started or resumed them.
16
+ - ab3e6e8: Give each threadless proactive Slack session a unique temporary continuation token so overlapping scheduled runs targeting the same channel do not conflict before they anchor to a Slack thread.
17
+ - a63dfa2: The dev TUI's `/vc` and `/login` commands are now `/vc:install` and `/vc:login`. `/vc:login` is the single Vercel authentication command: it logs in locally and, in remote (`eve dev --url`) sessions, recovers access with Vercel OIDC.
18
+
3
19
  ## 0.13.5
4
20
 
5
21
  ### Patch Changes
@@ -1,4 +1,6 @@
1
1
  import type { ChannelAdapter } from "#channel/adapter.js";
2
2
  import type { Runtime } from "#channel/types.js";
3
3
  import type { SendFn } from "#channel/routes.js";
4
- export declare function createSendFn<TState = undefined>(runtime: Runtime, adapter: ChannelAdapter<any>, channelName: string): SendFn<TState>;
4
+ export declare function createSendFn<TState = undefined>(runtime: Runtime, adapter: ChannelAdapter<any>, channelName: string, metadata?: {
5
+ readonly requestId?: string;
6
+ }): SendFn<TState>;
@@ -1 +1 @@
1
- import{createSession}from"#channel/session.js";import{createLogger}from"#internal/logging.js";import{isRuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{serializeUrlFilePart}from"#internal/attachments/url-refs.js";const log=createLogger(`channel.send`);function createSendFn(t,r,i){return async(a,o)=>{let s=o.auth,c=o.callback,l=o.mode??`conversation`,u=o.state,d=o.continuationToken,f=`${i}:${d}`,{message:p,inputResponses:m,context:h,outputSchema:g}=normalizeSendInput(a),_=serializeUrlFilePartsInMessage(p);try{let{sessionId:n}=await t.deliver({auth:s,continuationToken:f,payload:{inputResponses:m,message:_,context:h,outputSchema:g}});return createSession(n,d,t)}catch(e){isRuntimeNoActiveSessionError(e)||log.warn(`deliver failed, falling back to starting a new session`,{continuationToken:f})}if(m&&m.length>0)throw Error(`Cannot deliver inputResponses — the target session was not found via continuation token.`);let v=u?{...r,state:{...r.state,...u}}:r;return createSession((await t.run({adapter:v,auth:s,capabilities:l===`conversation`?{requestInput:!0}:void 0,channelName:i,callback:c,continuationToken:f,input:{message:_??``,context:h,outputSchema:g},mode:l})).sessionId,d,t)}}function serializeUrlFilePartsInMessage(e){if(e===void 0||typeof e==`string`)return e;let t=!1,n=e.map(e=>e.type===`file`&&e.data instanceof URL&&e.data.protocol!==`data:`?(t=!0,{...e,data:serializeUrlFilePart(e.data)}):e);return t?n:e}function normalizeSendInput(e){return typeof e==`string`||Array.isArray(e)?{message:e}:e}export{createSendFn};
1
+ import{createSession}from"#channel/session.js";import{createLogger}from"#internal/logging.js";import{isRuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{serializeUrlFilePart}from"#internal/attachments/url-refs.js";const log=createLogger(`channel.send`);function createSendFn(t,r,i,a={}){return async(o,s)=>{let c=s.auth,l=s.callback,u=s.mode??`conversation`,d=s.state,f=s.continuationToken,p=`${i}:${f}`,{message:m,inputResponses:h,context:g,outputSchema:_}=normalizeSendInput(o),v=serializeUrlFilePartsInMessage(m);try{let n={auth:c,continuationToken:p,requestId:a.requestId,payload:{inputResponses:h,message:v,context:g,outputSchema:_}},{sessionId:r}=await t.deliver(n);return createSession(r,f,t)}catch(e){isRuntimeNoActiveSessionError(e)||log.warn(`deliver failed, falling back to starting a new session`,{continuationToken:p})}if(h&&h.length>0)throw Error(`Cannot deliver inputResponses — the target session was not found via continuation token.`);let y={adapter:d?{...r,state:{...r.state,...d}}:r,auth:c,capabilities:u===`conversation`?{requestInput:!0}:void 0,channelName:i,callback:l,continuationToken:p,input:{message:v??``,context:g,outputSchema:_},mode:u,requestId:a.requestId};return createSession((await t.run(y)).sessionId,f,t)}}function serializeUrlFilePartsInMessage(e){if(e===void 0||typeof e==`string`)return e;let t=!1,n=e.map(e=>e.type===`file`&&e.data instanceof URL&&e.data.protocol!==`data:`?(t=!0,{...e,data:serializeUrlFilePart(e.data)}):e);return t?n:e}function normalizeSendInput(e){return typeof e==`string`||Array.isArray(e)?{message:e}:e}export{createSendFn};
@@ -87,6 +87,8 @@ export interface DeliverPayload {
87
87
  */
88
88
  export interface DeliverHookPayload {
89
89
  readonly auth?: SessionAuthContext | null;
90
+ /** Inbound channel request id used only for workflow attributes. */
91
+ readonly requestId?: string;
90
92
  readonly kind: "deliver";
91
93
  readonly payloads: readonly DeliverPayload[];
92
94
  }
@@ -193,6 +195,8 @@ export interface RunInput {
193
195
  * leave this undefined.
194
196
  */
195
197
  readonly capabilities?: SessionCapabilities;
198
+ /** Inbound channel request id used to correlate workflow attributes. */
199
+ readonly requestId?: string;
196
200
  /**
197
201
  * Optional terminal callback. When present, the runtime posts a single
198
202
  * callback when the session completes or fails.
@@ -229,6 +233,8 @@ export interface DeliverInput {
229
233
  * this field before calling the adapter's hooks.
230
234
  */
231
235
  readonly auth?: SessionAuthContext | null;
236
+ /** Inbound channel request id used to correlate workflow attributes. */
237
+ readonly requestId?: string;
232
238
  readonly continuationToken: string;
233
239
  readonly payload: DeliverPayload;
234
240
  }
@@ -20,6 +20,18 @@ function createEveContinueSessionRoutePath(sessionId) {
20
20
  return `${EVE_ROUTE_PREFIX}/session/${encodeURIComponent(sessionId)}`;
21
21
  }
22
22
 
23
+ //#endregion
24
+ //#region src/client/agent-info-error.ts
25
+ var AgentInfoResponseError = class extends Error {
26
+ issues;
27
+ constructor(issues = []) {
28
+ const detail = issues.length === 0 ? "" : ` (${issues.join("; ")})`;
29
+ super(`The server returned an unrecognized response from the eve agent info route.${detail}`);
30
+ this.name = "AgentInfoResponseError";
31
+ this.issues = issues;
32
+ }
33
+ };
34
+
23
35
  //#endregion
24
36
  //#region src/client/agent-info-schema.ts
25
37
  const source = z.object({
@@ -397,10 +409,12 @@ async function* readNdjsonStream(body) {
397
409
  const reader = body.getReader();
398
410
  const decoder = new TextDecoder();
399
411
  let buffer = "";
412
+ let reachedEof = false;
400
413
  try {
401
414
  while (true) {
402
415
  const result = await reader.read();
403
416
  if (result.done) {
417
+ reachedEof = true;
404
418
  buffer += decoder.decode();
405
419
  break;
406
420
  }
@@ -416,6 +430,7 @@ async function* readNdjsonStream(body) {
416
430
  const trailing = buffer.trim();
417
431
  if (trailing.length > 0) yield JSON.parse(trailing);
418
432
  } finally {
433
+ if (!reachedEof) await reader.cancel().catch(() => {});
419
434
  reader.releaseLock();
420
435
  }
421
436
  }
@@ -720,8 +735,17 @@ var Client = class {
720
735
  const body = await response.text();
721
736
  throw new ClientError(response.status, body);
722
737
  }
723
- const result = AgentInfoResultSchema.safeParse(await response.json());
724
- if (!result.success) throw new SyntaxError("The server returned an unrecognized response from the Eve agent info route.");
738
+ let payload;
739
+ try {
740
+ payload = await response.json();
741
+ } catch {
742
+ throw new AgentInfoResponseError();
743
+ }
744
+ const result = AgentInfoResultSchema.safeParse(payload);
745
+ if (!result.success) throw new AgentInfoResponseError(result.error.issues.slice(0, 5).map((issue) => {
746
+ const path = issue.path.join(".");
747
+ return path.length === 0 ? issue.message : `${path}: ${issue.message}`;
748
+ }));
725
749
  return result.data;
726
750
  }
727
751
  async fetch(path, init = {}) {
@@ -20,6 +20,18 @@ function createEveContinueSessionRoutePath(sessionId) {
20
20
  return `${EVE_ROUTE_PREFIX}/session/${encodeURIComponent(sessionId)}`;
21
21
  }
22
22
 
23
+ //#endregion
24
+ //#region src/client/agent-info-error.ts
25
+ var AgentInfoResponseError = class extends Error {
26
+ issues;
27
+ constructor(issues = []) {
28
+ const detail = issues.length === 0 ? "" : ` (${issues.join("; ")})`;
29
+ super(`The server returned an unrecognized response from the eve agent info route.${detail}`);
30
+ this.name = "AgentInfoResponseError";
31
+ this.issues = issues;
32
+ }
33
+ };
34
+
23
35
  //#endregion
24
36
  //#region src/client/agent-info-schema.ts
25
37
  const source = z.object({
@@ -397,10 +409,12 @@ async function* readNdjsonStream(body) {
397
409
  const reader = body.getReader();
398
410
  const decoder = new TextDecoder();
399
411
  let buffer = "";
412
+ let reachedEof = false;
400
413
  try {
401
414
  while (true) {
402
415
  const result = await reader.read();
403
416
  if (result.done) {
417
+ reachedEof = true;
404
418
  buffer += decoder.decode();
405
419
  break;
406
420
  }
@@ -416,6 +430,7 @@ async function* readNdjsonStream(body) {
416
430
  const trailing = buffer.trim();
417
431
  if (trailing.length > 0) yield JSON.parse(trailing);
418
432
  } finally {
433
+ if (!reachedEof) await reader.cancel().catch(() => {});
419
434
  reader.releaseLock();
420
435
  }
421
436
  }
@@ -720,8 +735,17 @@ var Client = class {
720
735
  const body = await response.text();
721
736
  throw new ClientError(response.status, body);
722
737
  }
723
- const result = AgentInfoResultSchema.safeParse(await response.json());
724
- if (!result.success) throw new SyntaxError("The server returned an unrecognized response from the Eve agent info route.");
738
+ let payload;
739
+ try {
740
+ payload = await response.json();
741
+ } catch {
742
+ throw new AgentInfoResponseError();
743
+ }
744
+ const result = AgentInfoResultSchema.safeParse(payload);
745
+ if (!result.success) throw new AgentInfoResponseError(result.error.issues.slice(0, 5).map((issue) => {
746
+ const path = issue.path.join(".");
747
+ return path.length === 0 ? issue.message : `${path}: ${issue.message}`;
748
+ }));
725
749
  return result.data;
726
750
  }
727
751
  async fetch(path, init = {}) {
@@ -39,7 +39,7 @@ export interface Block {
39
39
  * rendered dim beneath the headline, capped to a handful of lines.
40
40
  */
41
41
  detail?: string;
42
- /** Tool / connection lifecycle status. */
42
+ /** Tool, connection, or synthetic command lifecycle status. */
43
43
  status?: ToolStatus;
44
44
  /** When true, treat `body` as pre-styled and only wrap + indent it. */
45
45
  preformatted?: boolean;
@@ -1,7 +1,7 @@
1
1
  import{sliceVisible,visibleLength,wrapVisibleLine}from"./terminal-text.js";import{formatValuePretty,truncate}from"./tool-format.js";import{renderMarkdown}from"./markdown.js";import{isPromptControlCommand}from"./prompt-commands.js";function renderBlockLines(e,n,r,i){let a=nestingPrefix(e.depth??0,r);return renderBody(e,Math.max(8,n-visibleLength(a)),r,i).map(e=>`${a}${e}`)}function nestingPrefix(e,t){return e<=0?``:`${t.colors.orange(t.glyph.rule)} `.repeat(e)}function renderBody(e,t,n,r){switch(e.kind){case`user`:return renderUser(e,t,n);case`assistant`:case`subagent-step`:return renderProse(e,t,n);case`reasoning`:return renderReasoning(e,t,n);case`tool`:case`subagent-tool`:return renderTool(e,t,n,r);case`error`:return renderError(e,t,n);case`notice`:return renderNotice(e,t,n);case`warning`:return renderWarning(e,t,n);case`result`:return renderResult(e,t,n);case`flow`:return renderFlow(e,t,n);case`command`:return renderCommand(e,n);case`question`:case`connection-auth`:return renderPreformatted(e,t,n);case`sandbox`:return renderSandbox(e,t,n,r);case`log`:return renderLog(e,t,n,r);case`subagent`:return renderSubagentHeader(e,t,n);case`agent-header`:return(e.body??``).split(`
2
2
  `)}}function renderUser(e,t,n){let r=n.colors.cyan(n.glyph.user);return wrap(e.body??``,t-2).map(e=>`${r} ${e}`)}function renderProse(e,t,r){let i=[],o=e.kind===`subagent-step`,s=o?``:`${r.colors.bold(r.colors.white(r.glyph.brand))} `,c=o?``:` `;e.reasoning&&e.reasoning.trim().length>0&&i.push(...renderReasoningLines(e.reasoning,t,r));let l=(e.body??``).trim();return l.length===0&&i.length===0?[`${s}${r.colors.dim(`thinking${r.glyph.ellipsis}`)}`]:(l.length>0&&renderMarkdown(l).split(`
3
3
  `).flatMap(e=>wrapVisibleLine(e,t-c.length)).forEach((e,t)=>{t===0&&!o&&i.length===0?i.push(`${s}${e}`):i.push(`${c}${e}`)}),i.length>0?i:[`${s}`])}function renderReasoning(e,t,n){return e.collapsed?[`${n.colors.gray(n.glyph.reasoning)} ${n.colors.dim(`thinking`)}`]:renderReasoningLines(e.body??``,t,n,n.glyph.reasoning)}function renderReasoningLines(e,t,n,r){let i=r?2:0,a=wrap(e.trim(),t-i);return a.length===0?[]:a.map((e,t)=>`${r?t===0?`${n.colors.gray(r)} `:` `:``}${n.colors.dim(n.colors.italic(e))}`)}function renderTool(e,t,n,r){let{icon:a,accent:o}=toolGlyph(e.status??`running`,n,r),s=e.title??`tool`,c=t-2,l=truncatePlain(s,c),u=`${a} ${n.colors.bold(l)}`,d=c-l.length-2,f=e.subtitle??``;f.length>0&&d>=6&&(u+=` ${n.colors.gray(truncate(f,d))}`);let p=[u];return e.expanded?p.push(...renderToolExpanded(e,t,n)):e.status===`done`&&e.result&&e.result.length>0?p.push(resultLine(n.glyph.arrow,e.result,t,n,o)):e.status===`error`&&e.result?p.push(resultLine(n.glyph.arrow,e.result,t,n,n.colors.red)):e.status===`denied`&&p.push(resultLine(n.glyph.arrow,`denied`,t,n,n.colors.yellow)),p}function renderToolExpanded(e,t,n){let i=[],push=(e,a,o)=>{if(a!==void 0){i.push(` ${n.colors.dim(e)}`);for(let e of wrap(formatValuePretty(a),t-4))i.push(` ${o(e)}`)}};return push(`input`,e.toolInput,n.colors.gray),e.status===`error`&&e.result?push(`error`,e.result,n.colors.red):push(`output`,e.toolOutput,n.colors.gray),i}function resultLine(e,t,n,r,a){let o=n-4;return` ${r.colors.dim(e)} ${a(truncate(t,o))}`}function toolGlyph(e,t,n){switch(e){case`done`:return{icon:t.colors.green(t.glyph.success),accent:t.colors.gray};case`error`:return{icon:t.colors.red(t.glyph.error),accent:t.colors.red};case`denied`:return{icon:t.colors.yellow(t.glyph.warning),accent:t.colors.yellow};case`approval`:return{icon:t.colors.yellow(t.glyph.question),accent:t.colors.yellow};default:return{icon:t.colors.yellow(n.spinner),accent:t.colors.gray}}}function renderError(e,t,n){let r=n.colors.red(n.colors.bold(n.glyph.error)),i=e.title??`Error`,a=[`${r} ${n.colors.red(n.colors.bold(i))}`];for(let r of wrap(e.body??``,t-2))a.push(` ${colorizeError(r,n)}`);return a.push(...renderErrorDetail(e.detail,t,n)),a}function renderErrorDetail(e,t,n){if(e===void 0||e.trim().length===0)return[];let r=e.split(`
4
- `),i=r.slice(0,12),a=i.map(e=>` ${n.colors.dim(truncatePlain(e,Math.max(1,t-2)))}`),o=r.length-i.length;return o>0&&a.push(` ${n.colors.dim(`${n.glyph.ellipsis} +${o} more line${o===1?``:`s`}`)}`),a}const URL_PATTERN=/(https?:\/\/\S+)/u;function colorizeError(e,t){return URL_PATTERN.test(e)?e.split(URL_PATTERN).map((e,n)=>n%2==1?t.colors.cyan(e):t.colors.red(e)).join(``):t.colors.red(e)}function renderNotice(e,t,n){let r=n.colors.dim(n.glyph.dot),i=wrap(e.body??``,t-2);return i.length===0?[r]:i.map(e=>`${r} ${n.colors.dim(e)}`)}function renderAttentionRows(e,t,n){let r=n.colors.yellow(n.glyph.warning);return wrap(e,t-2).map((e,t)=>`${t===0?r:` `} ${paintCommands(e,n)}`)}function renderWarning(e,t,n){return renderAttentionRows(e.body??``,t,n)}function paintCommands(e,t){return e.replace(/\/[a-z-]+/g,e=>isPromptControlCommand(e)?t.colors.blue(e):e)}function renderCommand(e,t){let n=t.colors;return[`${n.cyan(t.glyph.user)} ${n.blue(e.body??``)}`]}function renderFlow(e,t,n){let r=n.colors,i=e.title??`info`,a=i===`success`?r.green(n.glyph.success):i===`warning`?r.yellow(n.glyph.warning):i===`error`?r.red(n.glyph.error):r.dim(n.glyph.dot),o=wrap(e.body??``,t-2),paint=e=>i===`info`?r.dim(e):e;return o.map((e,t)=>`${t===0?a:` `} ${paint(e)}`)}function renderResult(e,t,n){let r=n.colors.dim(n.glyph.elbow),i=wrap(e.body??``,t-7);if(i.length===0)return[` ${r}`];let dim=e=>n.colors.dim(e.replaceAll(`\x1B[22m`,`\x1B[22m\x1B[2m`));return i.map((e,t)=>t===0?` ${r} ${dim(e)}`:` ${dim(e)}`)}function renderPreformatted(e,t,r){let i=e.kind===`connection-auth`?r.colors.yellow(r.glyph.connection):r.colors.yellow(r.colors.bold(r.glyph.question)),a=wrap(e.title??``,t-2),o=a.length===0?[`${i} `]:a.map((e,t)=>t===0?`${i} ${r.colors.bold(e)}`:` ${r.colors.bold(e)}`);for(let r of(e.body??``).split(`
4
+ `),i=r.slice(0,12),a=i.map(e=>` ${n.colors.dim(truncatePlain(e,Math.max(1,t-2)))}`),o=r.length-i.length;return o>0&&a.push(` ${n.colors.dim(`${n.glyph.ellipsis} +${o} more line${o===1?``:`s`}`)}`),a}const URL_PATTERN=/(https?:\/\/\S+)/u;function colorizeError(e,t){return URL_PATTERN.test(e)?e.split(URL_PATTERN).map((e,n)=>n%2==1?t.colors.cyan(e):t.colors.red(e)).join(``):t.colors.red(e)}function renderNotice(e,t,n){let r=n.colors.dim(n.glyph.dot),i=wrap(e.body??``,t-2);return i.length===0?[r]:i.map(e=>`${r} ${n.colors.dim(e)}`)}function renderAttentionRows(e,t,n){let r=n.colors.yellow(n.glyph.warning);return wrap(e,t-2).map((e,t)=>`${t===0?r:` `} ${paintCommands(e,n)}`)}function renderWarning(e,t,n){return renderAttentionRows(e.body??``,t,n)}function paintCommands(e,t){return e.replace(/\/[a-z:-]+/g,e=>isPromptControlCommand(e)?t.colors.blue(e):e)}function renderCommand(e,t){let n=t.colors,r=e.status===`error`?`${n.red(t.glyph.error)} `:``;return[`${n.cyan(t.glyph.user)} ${r}${n.blue(e.body??``)}`]}function renderFlow(e,t,n){let r=n.colors,i=e.title??`info`,a=i===`success`?r.green(n.glyph.success):i===`warning`?r.yellow(n.glyph.warning):i===`error`?r.red(n.glyph.error):r.dim(n.glyph.dot),o=wrap(e.body??``,t-2),paint=e=>i===`info`?r.dim(e):e;return o.map((e,t)=>`${t===0?a:` `} ${paint(e)}`)}function renderResult(e,t,n){let r=n.colors.dim(n.glyph.elbow),i=wrap(e.body??``,t-7);if(i.length===0)return[` ${r}`];let dim=e=>n.colors.dim(e.replaceAll(`\x1B[22m`,`\x1B[22m\x1B[2m`));return i.map((e,t)=>t===0?` ${r} ${dim(e)}`:` ${dim(e)}`)}function renderPreformatted(e,t,r){let i=e.kind===`connection-auth`?r.colors.yellow(r.glyph.connection):r.colors.yellow(r.colors.bold(r.glyph.question)),a=wrap(e.title??``,t-2),o=a.length===0?[`${i} `]:a.map((e,t)=>t===0?`${i} ${r.colors.bold(e)}`:` ${r.colors.bold(e)}`);for(let r of(e.body??``).split(`
5
5
  `))for(let e of wrapVisibleLine(r,t-2))o.push(` ${e}`);return o}function renderSandbox(e,r,i,a){let o=i.colors.cyan(i.glyph.rule),s=i.colors.dim(`sandbox ${i.glyph.dot} `),c=visibleLength(s),l=` `.repeat(c),u=a.previous?.kind===`sandbox`,d=(e.body??``).split(`
6
6
  `),f=[];for(let e of d){let t=wrapVisibleLine(e,Math.max(1,r-2-c));for(let e of t){let t=f.length===0&&!u?s:l;f.push(`${o} ${t}${i.colors.gray(e)}`)}}return f.length>0?f:[`${o}`]}function renderLog(e,r,i,a){let o=e.title===`stderr`,s=o?i.colors.red:i.colors.gray,c=i.colors.dim(i.glyph.rule),l=o?`stderr`:`stdout`,u=i.colors.dim(`${l} ${i.glyph.dot} `),d=visibleLength(u),f=` `.repeat(d),p=a.previous?.kind===`log`&&a.previous.title===e.title,m=(e.body??``).split(`
7
7
  `),h=[];for(let e of m){let t=wrapVisibleLine(e,Math.max(1,r-2-d));for(let e of t){let t=h.length===0&&!p?u:f;h.push(`${c} ${t}${i.colors.dim(s(e))}`)}}return h.length>0?h:[`${c}`]}function renderSubagentHeader(e,t,n){let r=truncatePlain(e.title??`subagent`,Math.max(8,t-14));return[`${n.colors.orange(n.glyph.subagent)} ${n.colors.bold(r)} ${n.colors.dim(`subagent`)}`]}function wrap(e,t){return e.trim().length===0?[]:e.split(`
@@ -1,8 +1,10 @@
1
1
  import type { ApplyModelOutcome } from "#setup/flows/model.js";
2
2
  import type { PromptCommandHandler } from "./runner.js";
3
+ import type { RemoteAuthFlow } from "./remote-auth.js";
3
4
  import type { TuiSetupFlows } from "./setup-commands.js";
5
+ import type { DevelopmentTuiTarget } from "./target.js";
4
6
  export interface PromptCommandHandlerOptions {
5
- readonly appRoot?: string;
7
+ readonly target: DevelopmentTuiTarget;
6
8
  /** Test seam; defaults to the model flow's shared source-change apply. */
7
9
  readonly applyModel?: (input: {
8
10
  appRoot: string;
@@ -12,5 +14,7 @@ export interface PromptCommandHandlerOptions {
12
14
  readonly modelChangeRefusal?: (appRoot: string) => Promise<string | null>;
13
15
  /** Test seam; forwarded to runTuiSetupCommand's injectable flows. */
14
16
  readonly flows?: Partial<TuiSetupFlows>;
17
+ /** Test seam for remote authentication. */
18
+ readonly remoteAuthFlow?: RemoteAuthFlow;
15
19
  }
16
20
  export declare function createPromptCommandHandler(options: PromptCommandHandlerOptions): PromptCommandHandler;
@@ -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,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};
1
+ import{isPromptCommandAvailableFor}from"./prompt-commands.js";import{toErrorMessage}from"#shared/errors.js";function createPromptCommandHandler(n){return{async handle(r,i){let{target:a}=n;if(a.kind===`remote`&&!isPromptCommandAvailableFor(r.name,`remote`))return{message:`/${r.name} needs eve dev running the local server (it is not available with --url).`};if(r.name===`model`&&r.argument.length>0){if(a.kind!==`local`)return{message:`/model needs eve dev running the local server (it is not available with --url).`};let e=a.workspaceRoot;try{let{changeAgentModel:t,formatApplyModelOutcome:i,modelChangeRefusalForUneditableModel:a}=await import(`#setup/flows/model.js`),o=await(n.modelChangeRefusal??a)(e);return o===null?{message:i(await(n.applyModel??t)({appRoot:e,slug:r.argument}))}:{message:o}}catch(e){return{message:`Couldn't change the model: ${toErrorMessage(e)}`}}}let o=i.renderer.setupFlow;if(o===void 0)return{message:`/${r.name} is not supported by this renderer.`};if(r.name===`vc:login`&&a.kind===`remote`){if(i.remoteConnection===void 0)return{message:`/vc:login is not available in this session.`};let e;try{({runRemoteAuthCommand:e}=await import(`./remote-auth-command.js`))}catch(e){return{message:`/vc:login failed: ${toErrorMessage(e)}`}}return{message:await e({connection:i.remoteConnection,flow:n.remoteAuthFlow,renderer:o})}}let s;try{s=await import(`./setup-commands.js`)}catch(e){return{message:`/${r.name} failed: ${toErrorMessage(e)}`}}let{runTuiSetupCommand:c,SETUP_FLOW_CONFIG:l}=s,u=l[r.name];o.begin(u.title,u.indicator);let d=!0;try{let e={command:r.name,appRoot:a.workspaceRoot,renderer:o};i.initialModelStep!==void 0&&(e.initialModelStep=i.initialModelStep),n.flows!==void 0&&(e.flows=n.flows);let t=await c(e);d=t.preserveFlowDiagnostics;let s={message:t.message};return t.effect!==void 0&&(s.effect=t.effect),s}finally{o.end({preserveDiagnostics:d})}}}}export{createPromptCommandHandler};
@@ -1,4 +1,5 @@
1
- export type PromptCommandExtensionName = "model" | "channels" | "deploy" | "login" | "vc";
1
+ export type PromptCommandExtensionName = "model" | "channels" | "deploy" | "vc:install" | "vc:login";
2
+ type PromptCommandTarget = "local" | "remote";
2
3
  /** The slash commands the prompt accepts. */
3
4
  export type PromptCommand = {
4
5
  type: "new";
@@ -32,12 +33,10 @@ export interface PromptCommandSpec {
32
33
  /** Maps a recognized invocation to its parsed command. */
33
34
  readonly build: (argument: string) => PromptCommand;
34
35
  }
35
- /**
36
- * Every slash command the prompt accepts, in typeahead display order. One
37
- * module owns the command list so the runner's dispatch, the renderer's
38
- * transcript-echo suppression, and command discovery cannot drift apart.
39
- */
40
36
  export declare const PROMPT_COMMANDS: readonly PromptCommandSpec[];
37
+ export declare function promptCommandsFor(target: PromptCommandTarget): readonly PromptCommandSpec[];
38
+ /** Whether a command runs against this target — the one authority dispatch shares with discovery. */
39
+ export declare function isPromptCommandAvailableFor(name: PromptCommandExtensionName, target: PromptCommandTarget): boolean;
41
40
  /**
42
41
  * Recognizes the slash commands the prompt accepts. `/new` clears the
43
42
  * session and transcript; `/exit` (and `/quit`) terminate the TUI like
@@ -51,4 +50,5 @@ export declare function isPromptControlCommand(prompt: string): boolean;
51
50
  * The table `/help` prints: one line per command — slash name, argument
52
51
  * hint, and aliases padded into a column, description after.
53
52
  */
54
- export declare function formatPromptCommandHelp(): string;
53
+ export declare function formatPromptCommandHelp(commands?: readonly PromptCommandSpec[]): string;
54
+ export {};
@@ -1,2 +1,2 @@
1
- const PROMPT_COMMANDS=[{name:`help`,aliases:[],description:`Show available commands`,takesArgument:!1,build:()=>({type:`help`})},{name:`new`,aliases:[],description:`Start a fresh session`,takesArgument:!1,build:()=>({type:`new`})},{name:`vc`,aliases:[],description:`Install the Vercel CLI`,takesArgument:!1,build:()=>({type:`extension`,name:`vc`,argument:``})},{name:`login`,aliases:[],description:`Log in to Vercel`,takesArgument:!1,build:()=>({type:`extension`,name:`login`,argument:``})},{name:`model`,aliases:[],description:`Configure the agent's model and provider`,argumentHint:`[provider/model]`,takesArgument:!0,build:e=>({type:`extension`,name:`model`,argument:e})},{name:`loglevel`,aliases:[],description:`Show or hide captured stdout/stderr/sandbox logs`,argumentHint:`[all|stderr|sandbox|none]`,takesArgument:!0,build:e=>({type:`loglevel`,argument:e})},{name:`channels`,aliases:[],description:`Add chat channels to the agent`,takesArgument:!1,build:()=>({type:`extension`,name:`channels`,argument:``})},{name:`deploy`,aliases:[],description:`Deploy the agent to Vercel`,takesArgument:!1,build:()=>({type:`extension`,name:`deploy`,argument:``})},{name:`exit`,aliases:[`quit`],description:`Quit the TUI`,takesArgument:!1,build:()=>({type:`exit`})}];function parsePromptCommand(t){let n=t.trim();if(!n.startsWith(`/`))return null;for(let t of PROMPT_COMMANDS)for(let e of[t.name,...t.aliases]){let r=`/${e}`;if(n===r)return t.build(``);if(t.takesArgument&&n.startsWith(`${r} `))return t.build(n.slice(r.length).trim())}return null}function isPromptControlCommand(e){return parsePromptCommand(e)!==null}function formatPromptCommandHelp(){let t=PROMPT_COMMANDS.map(e=>{let t=e.argumentHint===void 0?``:` ${e.argumentHint}`,n=e.aliases.map(e=>` (/${e})`).join(``);return{invocation:`/${e.name}${t}${n}`,description:e.description}}),n=Math.max(...t.map(e=>e.invocation.length))+2;return t.map(e=>e.invocation.padEnd(n)+e.description).join(`
2
- `)}export{PROMPT_COMMANDS,formatPromptCommandHelp,isPromptControlCommand,parsePromptCommand};
1
+ const PROMPT_COMMAND_DEFINITIONS=[{name:`help`,aliases:[],description:`Show available commands`,takesArgument:!1,build:()=>({type:`help`}),targets:[`local`,`remote`]},{name:`new`,aliases:[],description:`Start a fresh session`,takesArgument:!1,build:()=>({type:`new`}),targets:[`local`,`remote`]},{name:`vc:install`,aliases:[],description:`Install the Vercel CLI`,takesArgument:!1,build:()=>({type:`extension`,name:`vc:install`,argument:``}),targets:[`local`,`remote`]},{name:`vc:login`,aliases:[],description:`Authenticate with Vercel`,takesArgument:!1,build:()=>({type:`extension`,name:`vc:login`,argument:``}),targets:[`local`,`remote`]},{name:`model`,aliases:[],description:`Configure the agent's model and provider`,argumentHint:`[provider/model]`,takesArgument:!0,build:e=>({type:`extension`,name:`model`,argument:e}),targets:[`local`]},{name:`loglevel`,aliases:[],description:`Show or hide captured stdout/stderr/sandbox logs`,argumentHint:`[all|stderr|sandbox|none]`,takesArgument:!0,build:e=>({type:`loglevel`,argument:e}),targets:[`local`,`remote`]},{name:`channels`,aliases:[],description:`Add chat channels to the agent`,takesArgument:!1,build:()=>({type:`extension`,name:`channels`,argument:``}),targets:[`local`]},{name:`deploy`,aliases:[],description:`Deploy the agent to Vercel`,takesArgument:!1,build:()=>({type:`extension`,name:`deploy`,argument:``}),targets:[`local`]},{name:`exit`,aliases:[`quit`],description:`Quit the TUI`,takesArgument:!1,build:()=>({type:`exit`}),targets:[`local`,`remote`]}],PROMPT_COMMANDS=PROMPT_COMMAND_DEFINITIONS;function promptCommandsFor(t){return PROMPT_COMMAND_DEFINITIONS.filter(e=>e.targets.some(e=>e===t))}function isPromptCommandAvailableFor(t,n){return(PROMPT_COMMAND_DEFINITIONS.find(e=>e.name===t)?.targets)?.includes(n)??!1}function parsePromptCommand(e){let n=e.trim();if(!n.startsWith(`/`))return null;for(let e of PROMPT_COMMANDS)for(let t of[e.name,...e.aliases]){let r=`/${t}`;if(n===r)return e.build(``);if(e.takesArgument&&n.startsWith(`${r} `))return e.build(n.slice(r.length).trim())}return null}function isPromptControlCommand(e){return parsePromptCommand(e)!==null}function formatPromptCommandHelp(e=PROMPT_COMMANDS){let n=e.map(e=>{let t=e.argumentHint===void 0?``:` ${e.argumentHint}`,n=e.aliases.map(e=>` (/${e})`).join(``);return{invocation:`/${e.name}${t}${n}`,description:e.description}}),r=Math.max(...n.map(e=>e.invocation.length))+2;return n.map(e=>e.invocation.padEnd(r)+e.description).join(`
2
+ `)}export{PROMPT_COMMANDS,formatPromptCommandHelp,isPromptCommandAvailableFor,isPromptControlCommand,parsePromptCommand,promptCommandsFor};
@@ -0,0 +1,11 @@
1
+ import { type RemoteAuthFlow } from "./remote-auth.js";
2
+ import type { RemoteConnectionController } from "./remote-connection.js";
3
+ import type { SetupFlowRenderer } from "./setup-flow.js";
4
+ interface RemoteAuthCommandInput {
5
+ readonly connection: RemoteConnectionController;
6
+ readonly flow?: RemoteAuthFlow;
7
+ readonly renderer: SetupFlowRenderer;
8
+ }
9
+ /** Runs remote `/vc:login` through one TUI panel, connection operation, and auth flow. */
10
+ export declare function runRemoteAuthCommand(input: RemoteAuthCommandInput): Promise<string>;
11
+ export {};
@@ -0,0 +1,3 @@
1
+ import{createTuiPrompter}from"./tui-prompter.js";import{describeRemoteAuthCompletedMutations}from"./remote-auth-result.js";import{runRemoteAuthFlow}from"./remote-auth.js";import{remoteHost}from"./target.js";import{WizardCancelledError}from"#setup/step.js";function shouldConfigureTrustedSources(e){switch(e.state){case`auth-required`:case`auth-failed`:return e.challenge.kind===`vercel-deployment-protection`;case`unavailable`:return e.failure.code===`TRUSTED_SOURCES_ENVIRONMENT_MISMATCH`;case`checking`:case`authenticating`:case`ready`:return!1}}function unavailableAfterAuthentication(e,t){let[n=t,...r]=t.split(/\n\s*\n/u);return[`Authentication was refreshed, but ${e} is unavailable: ${/[.!?]$/u.test(n.trim())?n.trim():`${n.trim()}.`}`,...r.map(e=>e.trim()).filter(e=>e.length>0)].join(`
2
+
3
+ `)}function mutedRenderer(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 runRemoteAuthCommand(i){i.renderer.begin(`Authenticate via Vercel OIDC`,`pulse`);let a=!0,o=!1,s=new AbortController,c=i.renderer.waitForInterrupt(),l=i.connection.current().target;try{let u=i.flow??runRemoteAuthFlow,d=shouldConfigureTrustedSources(i.connection.current().connection),f=createTuiPrompter(mutedRenderer(i.renderer,()=>o)),p=i.connection.authenticate(async e=>{let t=await u({workspaceRoot:l.workspaceRoot,serverUrl:l.serverUrl,configureTrustedSources:d,prompter:f,signal:e});return a=t.kind===`failed`,t},s.signal),m=await Promise.race([p.then(e=>({kind:`completed`,outcome:e})),c.promise.then(()=>({kind:`interrupted`}))]);m.kind===`interrupted`&&(o=!0,a=!0,s.abort(new WizardCancelledError));let h=m.kind===`completed`?m.outcome:await p;switch(h.kind){case`authenticated`:return`Authenticated ${remoteHost(l)} via Vercel OIDC.`;case`cancelled`:if(o){let e=describeRemoteAuthCompletedMutations(h.completedMutations);return e.length===0?`/vc:login interrupted.`:`/vc:login interrupted. Completed before interruption: ${e.join(`, `)}.`}return h.completedMutations.some(e=>e.kind===`vercel-login`)?`/vc:login cancelled after logging in to Vercel.`:`/vc:login cancelled.`;case`failed`:return h.message;case`unavailable`:return unavailableAfterAuthentication(remoteHost(l),h.failure.message)}}finally{c.dispose(),i.renderer.setStatus(void 0),i.renderer.end({preserveDiagnostics:a})}}export{runRemoteAuthCommand};
@@ -1,6 +1,9 @@
1
1
  import type { VerifiedVercelTarget } from "#setup/vercel-deployment.js";
2
2
  export type RemoteAuthCompletedMutation = {
3
3
  readonly kind: "vercel-login";
4
+ } | {
5
+ readonly kind: "trusted-sources-updated";
6
+ readonly targetProjectName: string;
4
7
  };
5
8
  export type RemoteAuthPreparation = {
6
9
  readonly kind: "prepared";
@@ -19,3 +22,5 @@ export type RemoteAuthPreparation = {
19
22
  export declare function describeRemoteAuthCompletedMutations(completedMutations: readonly RemoteAuthCompletedMutation[]): string[];
20
23
  /** Adds the mutations that cannot be rolled back to an authentication failure. */
21
24
  export declare function appendRemoteAuthMutationSummary(message: string, completedMutations: readonly RemoteAuthCompletedMutation[]): string;
25
+ /** Formats a Vercel Deployment Protection challenge for the remote TUI. */
26
+ export declare function formatRemoteAuthChallengeMessage(serverUrl: string): string;
@@ -1 +1,2 @@
1
- function describeRemoteAuthCompletedMutations(e){return e.map(e=>{switch(e.kind){case`vercel-login`:return`logged in to Vercel`}})}function appendRemoteAuthMutationSummary(e,t){let n=describeRemoteAuthCompletedMutations(t);return n.length===0?e:`${e} Completed before the failure: ${n.join(`, `)}.`}export{appendRemoteAuthMutationSummary,describeRemoteAuthCompletedMutations};
1
+ function describeRemoteAuthCompletedMutations(e){return e.map(e=>{switch(e.kind){case`vercel-login`:return`logged in to Vercel`;case`trusted-sources-updated`:return`updated Trusted Sources for ${e.targetProjectName}`}})}function appendRemoteAuthMutationSummary(e,t){let n=describeRemoteAuthCompletedMutations(t);return n.length===0?e:`${e} Completed before the failure: ${n.join(`, `)}.`}function formatRemoteAuthChallengeMessage(e){return[`Vercel Deployment Protection blocked the request to ${e}.`,``,"To access the deployment from `eve dev`, do one of:"," • Run `/vc:login` to authenticate this remote via Vercel OIDC.",` • Set VERCEL_AUTOMATION_BYPASS_SECRET to a Protection Bypass for`,` Automation token (Project Settings → Deployment Protection).`,` • Disable Deployment Protection on the target deployment.`,``,`Docs: https://vercel.com/docs/deployment-protection`].join(`
2
+ `)}export{appendRemoteAuthMutationSummary,describeRemoteAuthCompletedMutations,formatRemoteAuthChallengeMessage};
@@ -0,0 +1,30 @@
1
+ import { resolveDevelopmentOidcToken } from "#services/dev-client/request-headers.js";
2
+ import { runLoginFlow } from "#setup/flows/login.js";
3
+ import type { Prompter } from "#setup/prompter.js";
4
+ import { pickTeam } from "#setup/vercel-project.js";
5
+ import { resolveVercelDeployment } from "#setup/vercel-deployment.js";
6
+ import { applyVercelTrustedSourceAccess, prepareVercelTrustedSourceAccess } from "./vercel-trusted-sources.js";
7
+ import { type RemoteAuthPreparation } from "./remote-auth-result.js";
8
+ /** Injectable entry point for the remote authentication flow. */
9
+ export type RemoteAuthFlow = typeof runRemoteAuthFlow;
10
+ export interface RemoteAuthFlowDeps {
11
+ readonly runLoginFlow: typeof runLoginFlow;
12
+ readonly pickTeam: typeof pickTeam;
13
+ readonly resolveVercelDeployment: typeof resolveVercelDeployment;
14
+ readonly resolveOidcToken: typeof resolveDevelopmentOidcToken;
15
+ readonly prepareVercelTrustedSourceAccess: typeof prepareVercelTrustedSourceAccess;
16
+ readonly applyVercelTrustedSourceAccess: typeof applyVercelTrustedSourceAccess;
17
+ }
18
+ /**
19
+ * Authenticates a remote by resolving its deployment URL to the owning Vercel
20
+ * project, updating Trusted Sources when needed and keeping the resolved
21
+ * credential in this TUI session.
22
+ */
23
+ export declare function runRemoteAuthFlow(input: {
24
+ readonly workspaceRoot: string;
25
+ readonly serverUrl: string;
26
+ readonly configureTrustedSources?: boolean;
27
+ readonly prompter: Prompter;
28
+ readonly signal?: AbortSignal;
29
+ readonly deps?: Partial<RemoteAuthFlowDeps>;
30
+ }): Promise<RemoteAuthPreparation>;
@@ -0,0 +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{applyVercelTrustedSourceAccess,prepareVercelTrustedSourceAccess}from"./vercel-trusted-sources.js";import{appendRemoteAuthMutationSummary}from"./remote-auth-result.js";import{WizardCancelledError}from"#setup/step.js";import{toErrorMessage}from"#shared/errors.js";import{resolveDevelopmentOidcToken}from"#services/dev-client/request-headers.js";import{formatDevelopmentOidcTokenFailure}from"#services/dev-client/vercel-auth-error.js";import{runLoginFlow}from"#setup/flows/login.js";import{pickTeam}from"#setup/vercel-project.js";import{resolveVercelDeployment}from"#setup/vercel-deployment.js";var import_picocolors=__toESM(require_picocolors(),1);const defaultDeps={runLoginFlow,pickTeam,resolveVercelDeployment,resolveOidcToken:resolveDevelopmentOidcToken,prepareVercelTrustedSourceAccess,applyVercelTrustedSourceAccess};function failed(e,t=[]){return{kind:`failed`,message:appendRemoteAuthMutationSummary(e,t),completedMutations:[...t]}}function cancelled(e=[]){return{kind:`cancelled`,completedMutations:[...e]}}function loginFailure(e){switch(e.kind){case`already`:case`logged-in`:return;case`cancelled`:return cancelled();case`cli-missing`:return failed("The Vercel CLI is not installed. Install it with `npm i -g vercel@latest`, then retry /vc:login.");case`failed`:return failed(`Vercel login did not complete. Retry /vc:login.`);case`unavailable`:return failed(`Vercel could not verify your account. Check your connection, then retry /vc:login.`)}}function deploymentFailureMessage(e){return e.cause===`vercel`?e.failure.message:e.message}async function selectVercelDeploymentScope(e,t,n,r,i){return await e.pickTeam(r,t,void 0,{signal:i,selectMessage:e=>`${import_picocolors.default.blue(n)} is not accessible via current team (${import_picocolors.default.bold(e)})\nYou can try selecting another team:`})}async function runRemoteAuthFlow(e){let{workspaceRoot:t,serverUrl:n,prompter:r,signal:i}=e,a=new URL(n).host,o={...defaultDeps,...e.deps},s=[],c=!1;try{let n=await o.runLoginFlow({appRoot:t,prompter:r,signal:i}),l=loginFailure(n);if(l!==void 0)return l;n.kind===`logged-in`&&s.push({kind:`vercel-login`});let u=await o.resolveVercelDeployment({workspaceRoot:t,host:a,signal:i});if(u.kind===`forbidden`){let e=await o.runLoginFlow({appRoot:t,force:!0,prompter:r,signal:i}),n=loginFailure(e);if(n!==void 0)return n;e.kind===`logged-in`&&s.push({kind:`vercel-login`}),i?.throwIfAborted(),u=await o.resolveVercelDeployment({workspaceRoot:t,host:a,signal:i})}if(u.kind===`not-found`)try{let e=await selectVercelDeploymentScope(o,t,a,r,i);i?.throwIfAborted(),u=await o.resolveVercelDeployment({workspaceRoot:t,host:a,scope:e,signal:i})}catch(e){return e instanceof WizardCancelledError?cancelled(s):(i?.throwIfAborted(),failed(`Could not select a Vercel team: ${toErrorMessage(e)}`,s))}let d;switch(u.kind){case`resolved`:d=u.target;break;case`cancelled`:return cancelled(s);case`forbidden`:return failed(`Could not access ${a}. Re-authenticate (for example to complete a team's SSO), then retry /vc:login.`,s);case`not-found`:return failed(`Vercel did not resolve ${a} for the selected Vercel team.`,s);case`project-mismatch`:return failed(`Could not verify ${a}: Vercel resolved project ${u.actualProjectId}, not ${u.expectedProjectId}.`,s);case`failed`:return failed(`Could not verify ${a} through Vercel: ${deploymentFailureMessage(u.failure)}`,s)}let f;if(e.configureTrustedSources===!0){let e=await o.prepareVercelTrustedSourceAccess({workspaceRoot:t,target:d,prompter:r,signal:i});if(i?.throwIfAborted(),e.kind===`cancelled`)return cancelled(s);if(e.kind===`failed`)return failed(e.message,s);e.kind===`approved`&&(f=e.grant)}if(i?.throwIfAborted(),f!==void 0){c=!0;let e=await o.applyVercelTrustedSourceAccess({workspaceRoot:t,grant:f,signal:i});if(e.kind===`failed`)return failed(e.message,s);e.kind===`updated`&&s.push({kind:`trusted-sources-updated`,targetProjectName:e.targetProjectName})}let p=await o.resolveOidcToken({ownerId:d.deployment.ownerId,projectId:d.deployment.projectId,forceRefresh:!0});if(i?.throwIfAborted(),p.kind!==`resolved`)return failed(formatDevelopmentOidcTokenFailure(p),s);let m=p.token.trim();return{kind:`prepared`,target:d,resolveToken:async()=>{let e=await o.resolveOidcToken(d.deployment);return e.kind===`resolved`&&(m=e.token.trim()),m},completedMutations:[...s]}}catch(e){return!c&&(e instanceof WizardCancelledError||i?.aborted===!0)?cancelled(s):failed(`Could not authenticate ${a}: ${toErrorMessage(e)}`,s)}}export{runRemoteAuthFlow};
@@ -3,7 +3,9 @@ import type { RemoteConnectionState } from "./remote-connection-types.js";
3
3
  export type RemoteProbeResult = Extract<RemoteConnectionState, {
4
4
  state: "ready" | "auth-required" | "unavailable";
5
5
  }>;
6
- export declare function classifyRemoteError(error: unknown): RemoteProbeResult;
6
+ export type RemoteProbePhase = "connection-check" | "authentication-verification";
7
+ export declare function classifyRemoteError(error: unknown, phase: RemoteProbePhase): RemoteProbeResult;
7
8
  export declare function probeRemoteInfo(input: {
8
9
  readonly client: Client;
10
+ readonly phase: RemoteProbePhase;
9
11
  }): Promise<RemoteProbeResult>;
@@ -1 +1 @@
1
- import{isObject}from"#shared/guards.js";import{toErrorMessage}from"#shared/errors.js";import{ClientError}from"#client/index.js";import{isVercelAuthChallenge}from"#services/dev-client/vercel-auth-error.js";function isEveOidcChallenge(t){if(!(t instanceof ClientError)||t.status!==401)return!1;try{let n=JSON.parse(t.body);return isObject(n)&&n.ok===!1&&n.code===`unauthorized`&&n.error===`Authorization is required for this route.`}catch{return!1}}function classifyRemoteError(e){return isVercelAuthChallenge(e)?{state:`auth-required`,challenge:{kind:`vercel-deployment-protection`}}:isEveOidcChallenge(e)?{state:`auth-required`,challenge:{kind:`eve-oidc`}}:e instanceof ClientError?{state:`unavailable`,failure:{message:e.message}}:{state:`unavailable`,failure:{message:toErrorMessage(e)}}}async function probeRemoteInfo(e){try{return{state:`ready`,info:await e.client.info()}}catch(e){return classifyRemoteError(e)}}export{classifyRemoteError,probeRemoteInfo};
1
+ import{isObject}from"#shared/guards.js";import{toErrorMessage}from"#shared/errors.js";import{formatVercelTrustedSourcesFailure,isVercelAuthChallenge,vercelTrustedSourcesErrorCode}from"#services/dev-client/vercel-auth-error.js";import{AgentInfoResponseError,ClientError}from"#client/index.js";function isEveOidcChallenge(t){if(!(t instanceof ClientError)||t.status!==401)return!1;try{let n=JSON.parse(t.body);return isObject(n)&&n.ok===!1&&n.code===`unauthorized`&&n.error===`Authorization is required for this route.`}catch{return!1}}function classifyRemoteError(e,i){if(isVercelAuthChallenge(e))return{state:`auth-required`,challenge:{kind:`vercel-deployment-protection`}};if(isEveOidcChallenge(e))return{state:`auth-required`,challenge:{kind:`eve-oidc`}};if(e instanceof ClientError){let t=vercelTrustedSourcesErrorCode(e.message);if(i===`connection-check`&&e.status===403&&t===`TRUSTED_SOURCES_ENVIRONMENT_MISMATCH`)return{state:`auth-required`,challenge:{kind:`vercel-deployment-protection`}};let r={message:formatVercelTrustedSourcesFailure(e.message)};return{state:`unavailable`,failure:t===void 0?r:{...r,code:t}}}return{state:`unavailable`,failure:{message:toErrorMessage(e)}}}async function probeDeploymentHealth(t){try{let n=await t.health();return isObject(n)&&n.ok===!0}catch{return!1}}async function probeRemoteInfo(e){try{return{state:`ready`,info:await e.client.info()}}catch(t){return(t instanceof AgentInfoResponseError||t instanceof ClientError&&t.status===404)&&await probeDeploymentHealth(e.client)?{state:`ready`}:classifyRemoteError(t,e.phase)}}export{classifyRemoteError,probeRemoteInfo};
@@ -10,13 +10,14 @@ export type RemoteAuthChallenge = {
10
10
  readonly kind: "vercel-deployment-protection";
11
11
  };
12
12
  interface RemoteRequestFailure {
13
+ readonly code?: string;
13
14
  readonly message: string;
14
15
  }
15
16
  export type RemoteConnectionState = {
16
17
  readonly state: "checking";
17
18
  } | {
18
19
  readonly state: "ready";
19
- readonly info: AgentInfoResult;
20
+ readonly info?: AgentInfoResult;
20
21
  } | {
21
22
  readonly state: "auth-required";
22
23
  readonly challenge: RemoteAuthChallenge;
@@ -1 +1 @@
1
- import{appendRemoteAuthMutationSummary}from"./remote-auth-result.js";import{classifyRemoteError,probeRemoteInfo}from"./remote-connection-probe.js";import{remoteHost}from"./target.js";import{toErrorMessage}from"#shared/errors.js";function challengeFor(e){switch(e.state){case`auth-required`:case`authenticating`:case`auth-failed`:return e.challenge;case`checking`:case`ready`:case`unavailable`:return{kind:`eve-oidc`}}}function createRemoteConnectionController(i){let a={state:`checking`},o,s,restoreActiveCredentials,c=0,l=!1,snapshot=()=>o===void 0?{target:i.target,connection:a}:{target:i.target,connection:a,deployment:o},update=e=>(a=e,l||i.onChange(snapshot()),e),beginOperation=e=>{s?.abort();let t=new AbortController;s=t;let n=e===void 0?t.signal:AbortSignal.any([t.signal,e]);return{generation:++c,signal:n}},isCurrent=e=>!l&&e===c,publishTarget=e=>{o=e.deployment,l||i.onChange(snapshot())},authorizeResolvedTarget=async(e,t)=>{let n=i.resolveDeployment;if(n!==void 0)try{let r=await n(e);return!isCurrent(t)||e.aborted||r.kind!==`resolved`?void 0:(publishTarget(r.target),i.credentials.authorize({target:r.target,resolveToken:async()=>await i.resolveOidcToken?.(r.target.deployment)??``}))}catch{}};return i.onChange(snapshot()),{current:snapshot,async check(){let e=beginOperation();restoreActiveCredentials?.(),restoreActiveCredentials=void 0,o=void 0,update({state:`checking`});let t=await authorizeResolvedTarget(e.signal,e.generation);if(!isCurrent(e.generation))return t?.(),a;restoreActiveCredentials=t;let r=await probeRemoteInfo({client:i.client});return isCurrent(e.generation)?update(r):a},async authenticate(t,o){let s=beginOperation(o),c=a,l=challengeFor(a);update({state:`authenticating`,challenge:l});let u;try{u=await t(s.signal)}catch(e){u={kind:`failed`,message:toErrorMessage(e),completedMutations:[]}}if(!isCurrent(s.generation))return{kind:`cancelled`,completedMutations:u.completedMutations};if(u.kind===`cancelled`)return update(c),{kind:`cancelled`,completedMutations:u.completedMutations};if(u.kind===`failed`)return update(c.state===`ready`?c:{state:`auth-failed`,challenge:l}),{kind:`failed`,message:u.message};if(s.signal.aborted)return update(c),{kind:`cancelled`,completedMutations:u.completedMutations};let d=restoreActiveCredentials,f;try{f=i.credentials.authorize({target:u.target,resolveToken:u.resolveToken})}catch(t){let n=appendRemoteAuthMutationSummary(toErrorMessage(t),u.completedMutations);return update({state:`auth-failed`,challenge:l}),{kind:`failed`,message:n}}let p=await probeRemoteInfo({client:i.client});if(!isCurrent(s.generation))return f(),{kind:`cancelled`,completedMutations:u.completedMutations};if(s.signal.aborted)return f(),update(c),{kind:`cancelled`,completedMutations:u.completedMutations};if(p.state===`ready`)return publishTarget(u.target),restoreActiveCredentials=()=>{f(),d?.()},update(p),{kind:`authenticated`};if(f(),p.state===`auth-required`){let t=appendRemoteAuthMutationSummary(`The selected Vercel project did not authorize ${remoteHost(i.target)}.`,u.completedMutations);return update({state:`auth-failed`,challenge:p.challenge}),{kind:`failed`,message:t}}let m={...p.failure,message:appendRemoteAuthMutationSummary(p.failure.message,u.completedMutations)};return update({state:`unavailable`,failure:m}),{kind:`unavailable`,failure:m}},reportFailure(e){return s?.abort(),c+=1,update(classifyRemoteError(e))},dispose(){l=!0,c+=1,s?.abort(),s=void 0,restoreActiveCredentials?.(),restoreActiveCredentials=void 0}}}export{createRemoteConnectionController};
1
+ import{appendRemoteAuthMutationSummary}from"./remote-auth-result.js";import{remoteHost}from"./target.js";import{classifyRemoteError,probeRemoteInfo}from"./remote-connection-probe.js";import{toErrorMessage}from"#shared/errors.js";function challengeFor(e){switch(e.state){case`auth-required`:case`authenticating`:case`auth-failed`:return e.challenge;case`checking`:case`ready`:return{kind:`eve-oidc`};case`unavailable`:return e.failure.code===`TRUSTED_SOURCES_ENVIRONMENT_MISMATCH`?{kind:`vercel-deployment-protection`}:{kind:`eve-oidc`}}}function createRemoteConnectionController(i){let a={state:`checking`},o,s,restoreActiveCredentials,c=0,l=!1,snapshot=()=>o===void 0?{target:i.target,connection:a}:{target:i.target,connection:a,deployment:o},update=e=>(a=e,l||i.onChange(snapshot()),e),beginOperation=e=>{s?.abort();let t=new AbortController;s=t;let n=e===void 0?t.signal:AbortSignal.any([t.signal,e]);return{generation:++c,signal:n}},isCurrent=e=>!l&&e===c,publishTarget=e=>{o=e.deployment,l||i.onChange(snapshot())},authorizeResolvedTarget=async(e,t)=>{let n=i.resolveDeployment;if(n!==void 0)try{let r=await n(e);return!isCurrent(t)||e.aborted||r.kind!==`resolved`?void 0:(publishTarget(r.target),i.credentials.authorize({target:r.target,resolveToken:async()=>await i.resolveOidcToken?.(r.target.deployment)??``}))}catch{}};return i.onChange(snapshot()),{current:snapshot,async check(){let e=beginOperation();restoreActiveCredentials?.(),restoreActiveCredentials=void 0,o=void 0,update({state:`checking`});let t=await authorizeResolvedTarget(e.signal,e.generation);if(!isCurrent(e.generation))return t?.(),a;restoreActiveCredentials=t;let n=await probeRemoteInfo({client:i.client,phase:`connection-check`});return isCurrent(e.generation)?update(n):a},async authenticate(n,o){let s=beginOperation(o),c=a,l=challengeFor(a);update({state:`authenticating`,challenge:l});let u;try{u=await n(s.signal)}catch(e){u={kind:`failed`,message:toErrorMessage(e),completedMutations:[]}}if(!isCurrent(s.generation))return{kind:`cancelled`,completedMutations:u.completedMutations};if(u.kind===`cancelled`)return update(c),{kind:`cancelled`,completedMutations:u.completedMutations};if(u.kind===`failed`)return update(c.state===`ready`?c:{state:`auth-failed`,challenge:l}),{kind:`failed`,message:u.message};if(s.signal.aborted)return update(c),{kind:`cancelled`,completedMutations:u.completedMutations};let d=restoreActiveCredentials,f;try{f=i.credentials.authorize({target:u.target,resolveToken:u.resolveToken})}catch(t){let n=appendRemoteAuthMutationSummary(toErrorMessage(t),u.completedMutations);return update({state:`auth-failed`,challenge:l}),{kind:`failed`,message:n}}let p=await probeRemoteInfo({client:i.client,phase:`authentication-verification`});if(!isCurrent(s.generation))return f(),{kind:`cancelled`,completedMutations:u.completedMutations};if(s.signal.aborted)return f(),update(c),{kind:`cancelled`,completedMutations:u.completedMutations};if(p.state===`ready`)return publishTarget(u.target),restoreActiveCredentials=()=>{f(),d?.()},update(p),{kind:`authenticated`};if(f(),p.state===`auth-required`){let n=appendRemoteAuthMutationSummary(`The selected Vercel project did not authorize ${remoteHost(i.target)}.`,u.completedMutations);return update({state:`auth-failed`,challenge:p.challenge}),{kind:`failed`,message:n}}let m={...p.failure,message:appendRemoteAuthMutationSummary(p.failure.message,u.completedMutations)};return update({state:`unavailable`,failure:m}),{kind:`unavailable`,failure:m}},reportFailure(e){return s?.abort(),c+=1,update(classifyRemoteError(e,`connection-check`))},dispose(){l=!0,c+=1,s?.abort(),s=void 0,restoreActiveCredentials?.(),restoreActiveCredentials=void 0}}}export{createRemoteConnectionController};
@@ -1,8 +1,11 @@
1
1
  import { type AgentInfoResult, type ConnectionAuthorizationOutcome, type InputRequest, Client, ClientSession } from "#client/index.js";
2
2
  import { type DevBootProgressReporter } from "#internal/dev-boot-progress.js";
3
- import { type PromptCommand } from "./prompt-commands.js";
3
+ import { type PromptCommand, type PromptCommandSpec } from "./prompt-commands.js";
4
+ import { type RemoteConnectionController, type RemoteConnectionControllerOptions, type RemoteConnectionSnapshot } from "./remote-connection.js";
5
+ import type { DevelopmentCredentialGate } from "#services/dev-client/credential-gate.js";
4
6
  import { type BootDetection } from "./setup-issues.js";
5
7
  import type { SetupFlowRenderer } from "./setup-flow.js";
8
+ import type { RemoteDevelopmentTarget } from "./target.js";
6
9
  import type { AssistantResponseStatsMode, LogDisplayMode, TerminalPartDisplayMode, TuiDisplayOptions } from "./types.js";
7
10
  import { type TerminalInput, type TerminalOutput } from "./terminal-renderer.js";
8
11
  import { type VercelStatusEffect, type VercelStatusSnapshot } from "./vercel-status.js";
@@ -141,6 +144,8 @@ export type AgentTUIRenderer = {
141
144
  renderSetupWarning?(text: string): void;
142
145
  /** Clears the setup attention line once its issue is resolved. */
143
146
  clearSetupWarning?(): void;
147
+ /** Commits the startup `/vc:login` invocation to the transcript. */
148
+ renderCommandInvocation?(text: string, status?: "failed"): void;
144
149
  renderCommandResult?(text: string): void;
145
150
  readonly setupFlow?: SetupFlowRenderer;
146
151
  readPrompt?(options?: AgentTUISessionOptions): Promise<string | undefined>;
@@ -206,6 +211,8 @@ export type AgentTUIRenderer = {
206
211
  * line ignore it.
207
212
  */
208
213
  setVercelStatus?(status: VercelStatusSnapshot): void;
214
+ /** Sets the remote deployment badge and its current connection/authentication state. */
215
+ setRemoteConnectionStatus?(status: RemoteConnectionSnapshot): void;
209
216
  /**
210
217
  * Clears the rendered transcript and resets per-conversation display
211
218
  * state, leaving the UI interactive on a fresh screen. Used by the
@@ -223,6 +230,7 @@ export interface PromptCommandHandlerContext {
223
230
  readonly title: string;
224
231
  /** Provider entry authorized by confirmed boot-time model-access evidence. */
225
232
  readonly initialModelStep?: "provider";
233
+ readonly remoteConnection?: RemoteConnectionController;
226
234
  }
227
235
  /** What one handled slash command leaves behind for the runner to apply. */
228
236
  export interface PromptCommandOutcome {
@@ -275,6 +283,15 @@ export type EveTUIRunnerOptions = TuiDisplayOptions & {
275
283
  initialInput?: string;
276
284
  /** Handles non-core slash commands without adding feature branches to the runner. */
277
285
  promptCommandHandler?: PromptCommandHandler;
286
+ /** Commands shown in discovery for this local or remote session. */
287
+ availablePromptCommands?: readonly PromptCommandSpec[];
288
+ /** Remote target and mutable OIDC token source, when connected through `--url`. */
289
+ remote?: {
290
+ readonly target: RemoteDevelopmentTarget;
291
+ readonly credentials: DevelopmentCredentialGate;
292
+ readonly resolveOidcToken: NonNullable<RemoteConnectionControllerOptions["resolveOidcToken"]>;
293
+ readonly resolveDeployment: NonNullable<RemoteConnectionControllerOptions["resolveDeployment"]>;
294
+ };
278
295
  /** Boot-time installation-state checks; defaults to the built-ins. */
279
296
  bootDetections?: readonly BootDetection[];
280
297
  /** Test seam for the status line's Vercel link probe; defaults to the real one. */