eve 0.9.1 → 0.9.3

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 (122) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/bin/eve.js +1 -0
  3. package/dist/src/cli/agent-detection.d.ts +8 -0
  4. package/dist/src/cli/agent-detection.js +1 -0
  5. package/dist/src/cli/commands/agent-instructions.d.ts +7 -0
  6. package/dist/src/cli/commands/agent-instructions.js +1 -0
  7. package/dist/src/cli/commands/init-agent-handoff.md +12 -0
  8. package/dist/src/cli/commands/init-agent-instructions.md +16 -0
  9. package/dist/src/cli/commands/init-git.js +1 -1
  10. package/dist/src/cli/commands/init.d.ts +8 -1
  11. package/dist/src/cli/commands/init.js +1 -1
  12. package/dist/src/cli/dev/environment.js +1 -1
  13. package/dist/src/cli/dev/tui/blocks.d.ts +8 -0
  14. package/dist/src/cli/dev/tui/blocks.js +2 -2
  15. package/dist/src/cli/dev/tui/command-typeahead.js +1 -1
  16. package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
  17. package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
  18. package/dist/src/cli/dev/tui/runner.d.ts +5 -0
  19. package/dist/src/cli/dev/tui/runner.js +1 -1
  20. package/dist/src/cli/dev/tui/setup-commands.d.ts +4 -0
  21. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  22. package/dist/src/cli/dev/tui/setup-issues.d.ts +15 -0
  23. package/dist/src/cli/dev/tui/setup-issues.js +1 -1
  24. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +7 -2
  25. package/dist/src/cli/dev/tui/terminal-renderer.js +9 -9
  26. package/dist/src/cli/run.js +1 -1
  27. package/dist/src/compiled/.vendor-stamp.json +11 -10
  28. package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -2
  29. package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
  30. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  31. package/dist/src/compiled/@ai-sdk/openai/index.js +2 -2
  32. package/dist/src/compiled/@vercel/detect-agent/LICENSE +202 -0
  33. package/dist/src/compiled/@vercel/detect-agent/index.d.ts +20 -0
  34. package/dist/src/compiled/@vercel/detect-agent/index.js +1 -0
  35. package/dist/src/compiled/@workflow/core/index.js +2 -2
  36. package/dist/src/compiled/@workflow/core/private.js +1 -1
  37. package/dist/src/compiled/@workflow/core/runtime/start.d.ts +13 -0
  38. package/dist/src/compiled/@workflow/core/runtime.js +28 -28
  39. package/dist/src/compiled/@workflow/core/telemetry/semantic-conventions.d.ts +4 -0
  40. package/dist/src/compiled/@workflow/core/telemetry.d.ts +57 -0
  41. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  42. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  43. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  44. package/dist/src/compiled/@workflow/world/queue.d.ts +3 -0
  45. package/dist/src/compiled/_chunks/workflow/{attribute-changes-DGVGRGfw.js → attribute-changes-Db-CKuLh.js} +10 -10
  46. package/dist/src/compiled/_chunks/workflow/dist-DhA2lbtB.js +14 -0
  47. package/dist/src/compiled/_chunks/workflow/{dist-6a3viBXZ.js → dist-DvHFhSJM.js} +1 -1
  48. package/dist/src/compiled/_chunks/workflow/dist-aOKD_2KN.js +1 -0
  49. package/dist/src/compiled/_chunks/workflow/resume-hook-Uk1RAmYo.js +1 -0
  50. package/dist/src/compiled/_chunks/workflow/run-AA-FTD_U.js +1 -0
  51. package/dist/src/compiled/_chunks/workflow/sleep-BG6X2Bli.js +1 -0
  52. package/dist/src/execution/sandbox/bindings/docker-templates.js +1 -1
  53. package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
  54. package/dist/src/execution/sandbox/bindings/microsandbox-templates.js +1 -1
  55. package/dist/src/internal/application/compiled-artifacts.js +1 -1
  56. package/dist/src/internal/application/optional-package-install.js +1 -1
  57. package/dist/src/internal/application/package.js +1 -1
  58. package/dist/src/internal/application/paths.js +1 -1
  59. package/dist/src/internal/application/tsconfig-dependencies.js +1 -1
  60. package/dist/src/internal/authored-module-loader.js +2 -2
  61. package/dist/src/internal/bundler/nitro-rolldown.d.ts +3 -0
  62. package/dist/src/internal/bundler/nitro-rolldown.js +1 -1
  63. package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
  64. package/dist/src/internal/nitro/dev-runtime-source-snapshot-copy.js +1 -1
  65. package/dist/src/internal/nitro/dev-runtime-source-snapshot.js +1 -1
  66. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  67. package/dist/src/internal/nitro/host/start-development-server.d.ts +1 -2
  68. package/dist/src/internal/nitro/host/start-development-server.js +2 -2
  69. package/dist/src/internal/nitro/host/start-production-server.js +2 -2
  70. package/dist/src/internal/process/pnpm.js +1 -1
  71. package/dist/src/internal/workflow-bundle/builder-support.js +2 -2
  72. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
  73. package/dist/src/internal/workflow-bundle/workflow-builders.js +1 -1
  74. package/dist/src/internal/workflow-bundle/workflow-transformer.js +3 -3
  75. package/dist/src/public/next/server.js +1 -1
  76. package/dist/src/setup/boxes/add-channels.js +1 -1
  77. package/dist/src/setup/boxes/apply-ai-gateway-credential.js +1 -1
  78. package/dist/src/setup/boxes/link-project.js +1 -1
  79. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  80. package/dist/src/setup/flows/channels.d.ts +2 -0
  81. package/dist/src/setup/flows/channels.js +1 -1
  82. package/dist/src/setup/flows/deploy.js +1 -1
  83. package/dist/src/setup/flows/install-vercel-cli.d.ts +42 -0
  84. package/dist/src/setup/flows/install-vercel-cli.js +1 -0
  85. package/dist/src/setup/flows/link.js +1 -1
  86. package/dist/src/setup/flows/login.d.ts +49 -0
  87. package/dist/src/setup/flows/login.js +1 -0
  88. package/dist/src/setup/flows/model.js +1 -1
  89. package/dist/src/setup/flows/vercel.d.ts +2 -0
  90. package/dist/src/setup/flows/vercel.js +1 -1
  91. package/dist/src/setup/primitives/coding-agent-env.d.ts +17 -0
  92. package/dist/src/setup/primitives/coding-agent-env.js +1 -0
  93. package/dist/src/setup/primitives/pm/pnpm.js +2 -2
  94. package/dist/src/setup/primitives/pm/run.d.ts +6 -0
  95. package/dist/src/setup/primitives/pm/run.js +1 -1
  96. package/dist/src/setup/primitives/run-vercel.d.ts +1 -1
  97. package/dist/src/setup/primitives/run-vercel.js +1 -1
  98. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  99. package/dist/src/setup/scaffold/create/project.js +2 -2
  100. package/dist/src/setup/scaffold/update/channels.js +1 -1
  101. package/dist/src/setup/scaffold/version-tokens.js +1 -1
  102. package/dist/src/setup/slack-connect-create.d.ts +41 -0
  103. package/dist/src/setup/slack-connect-create.js +1 -0
  104. package/dist/src/setup/slack-connect-lifecycle.d.ts +40 -11
  105. package/dist/src/setup/slack-connect-lifecycle.js +1 -1
  106. package/dist/src/setup/slack-connect.d.ts +25 -14
  107. package/dist/src/setup/slack-connect.js +1 -1
  108. package/dist/src/setup/slackbot.d.ts +12 -16
  109. package/dist/src/setup/slackbot.js +1 -1
  110. package/dist/src/setup/vercel-project.d.ts +25 -0
  111. package/dist/src/setup/vercel-project.js +1 -1
  112. package/dist/src/source-change/apply-model-name.d.ts +2 -2
  113. package/dist/src/source-change/apply-model-name.js +1 -1
  114. package/dist/src/source-change/static-source-change.js +1 -1
  115. package/docs/getting-started.mdx +5 -5
  116. package/docs/guides/dev-tui.md +3 -1
  117. package/package.json +16 -16
  118. package/dist/src/compiled/_chunks/workflow/dist-CkMRLaRV.js +0 -14
  119. package/dist/src/compiled/_chunks/workflow/dist-D0jyrm7a.js +0 -1
  120. package/dist/src/compiled/_chunks/workflow/resume-hook-DMSadN9o.js +0 -1
  121. package/dist/src/compiled/_chunks/workflow/run-BRdn7zy_.js +0 -1
  122. package/dist/src/compiled/_chunks/workflow/sleep-CpXfoXLF.js +0 -1
@@ -1,21 +1,39 @@
1
1
  /**
2
2
  * Pure parsers for Vercel Connect CLI/API payloads used by Slackbot
3
3
  * provisioning: connector-list shapes, the `connect create` stdout, and the
4
- * installations response. No subprocesses or I/O — just shape validation — so
5
- * the provisioning orchestrator ({@link import("./slackbot.js")}) stays focused
6
- * on flow and these stay trivially testable.
4
+ * connector detail response. No subprocesses or I/O — just shape validation —
5
+ * so the provisioning orchestrator ({@link import("./slackbot.js")}) stays
6
+ * focused on flow and these stay trivially testable.
7
7
  */
8
+ import { z } from "zod";
8
9
  export interface VercelConnectListResponse {
9
10
  /** `vercel connect list -F json` (current CLI). */
10
11
  connectors?: unknown;
11
12
  /** Older CLI builds emitted the same array under `clients`. */
12
13
  clients?: unknown;
13
14
  }
15
+ declare const SlackConnectorRefSchema: z.ZodObject<{
16
+ uid: z.ZodString;
17
+ id: z.ZodString;
18
+ }, z.core.$strip>;
14
19
  /** Identifiers returned by Vercel Connect for a Slack connector. */
15
- export interface SlackConnectorRef {
16
- uid: string;
17
- id: string;
20
+ export type SlackConnectorRef = z.infer<typeof SlackConnectorRefSchema>;
21
+ /** Slack workspace metadata exposed by a connected Slack connector. */
22
+ export interface SlackWorkspaceConnection {
23
+ workspaceUrl: string;
24
+ workspaceName?: string;
25
+ }
26
+ /** Parsed Slack connector state returned by Vercel's connector detail API. */
27
+ export interface SlackConnectorDetails {
28
+ ref: SlackConnectorRef;
29
+ workspace?: SlackWorkspaceConnection;
18
30
  }
31
+ /**
32
+ * Parses the exact connector response. Vercel reports a completed Slack
33
+ * workspace connection in `data.slackTeam`; its installations collection can
34
+ * remain empty even after browser setup succeeds.
35
+ */
36
+ export declare function parseSlackConnectorDetails(body: unknown): SlackConnectorDetails | undefined;
19
37
  /**
20
38
  * Reads the connector identifiers from `vercel connect create … -F json`
21
39
  * stdout, the authoritative source for the just-created connector's UID.
@@ -26,14 +44,6 @@ export declare function parseCreatedSlackConnector(stdout: string): SlackConnect
26
44
  * Finds the expected connector, or the newest Slack connector already attached to this project.
27
45
  */
28
46
  export declare function pickSlackConnector(listJson: unknown, projectId: string | undefined, expectedUid: string | undefined): SlackConnectorRef | undefined;
29
- export interface ConnectInstallationsResponse {
30
- installations?: unknown;
31
- }
32
- /** Extracts Slack workspace information from a Connect installations response. */
33
- export declare function parseInstallation(body: unknown): {
34
- workspaceUrl: string;
35
- workspaceName?: string;
36
- } | undefined;
37
47
  /**
38
48
  * Turns a Slack `app_redirect` install URL into a deep link that opens the
39
49
  * Messages tab, a DM compose with the bot, instead of the app's about page.
@@ -49,3 +59,4 @@ export interface RawSlackConnector {
49
59
  }
50
60
  /** Parses Slack connectors (uid + attached project ids) from a connect-list response. */
51
61
  export declare function parseSlackConnectors(listJson: unknown): RawSlackConnector[];
62
+ export {};
@@ -1 +1 @@
1
- function parseCreatedSlackConnector(e){let t=e.trim();if(!t)return;let n;try{n=JSON.parse(t)}catch{return}if(typeof n!=`object`||!n)return;let{uid:r,id:i}=n;if(!(typeof r!=`string`||typeof i!=`string`))return{uid:r,id:i}}function pickSlackConnector(e,t,n){if(t===void 0||typeof e!=`object`||!e)return;let r=e,i=r.connectors??r.clients;if(!Array.isArray(i))return;let a;for(let e of i){if(e.type!==`slack`||typeof e.uid!=`string`||typeof e.id!=`string`||!Array.isArray(e.projects)||!e.projects.some(e=>typeof e==`object`&&!!e&&e.id===t))continue;let r={uid:e.uid,id:e.id};if(n!==void 0&&r.uid===n)return r;let i=typeof e.createdAt==`number`?e.createdAt:0;(!a||i>a.createdAt)&&(a={ref:r,createdAt:i})}return a?.ref}function parseInstallation(e){if(typeof e!=`object`||!e)return;let t=e.installations;if(!Array.isArray(t)||t.length===0)return;let n=t[0];if(typeof n.tenantUrl==`string`)return{workspaceUrl:n.tenantUrl,workspaceName:typeof n.tenantName==`string`?n.tenantName:void 0}}function slackMessageDeepLink(e){let t=URL.parse(e);return t===null||!t.pathname.endsWith(`/app_redirect`)||!t.searchParams.has(`app`)||!t.searchParams.has(`team`)?e:(t.searchParams.set(`tab`,`messages`),t.href)}function parseSlackConnectors(e){if(typeof e!=`object`||!e)return[];let t=e,n=t.connectors??t.clients;if(!Array.isArray(n))return[];let r=[];for(let e of n){if(e.type!==`slack`||typeof e.uid!=`string`)continue;let t=Array.isArray(e.projects)?e.projects.map(e=>typeof e==`object`&&e?e.id:void 0).filter(e=>typeof e==`string`):[];r.push({uid:e.uid,projectIds:t})}return r}export{parseCreatedSlackConnector,parseInstallation,parseSlackConnectors,pickSlackConnector,slackMessageDeepLink};
1
+ import{object,string}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";const NonEmptyStringSchema=string().min(1),SlackConnectorDetailsSchema=object({uid:NonEmptyStringSchema,id:NonEmptyStringSchema}).extend({data:object({appId:NonEmptyStringSchema.nullish(),slackTeam:object({id:NonEmptyStringSchema,name:NonEmptyStringSchema.nullish()}).nullish()}).nullish()});function parseSlackConnectorDetails(e){let t=SlackConnectorDetailsSchema.safeParse(e);if(!t.success)return;let{id:n,uid:i,data:a}=t.data,o={id:n,uid:i};if(a?.appId==null||a.slackTeam==null)return{ref:o};let s=new URL(`https://slack.com/app_redirect`);return s.searchParams.set(`app`,a.appId),s.searchParams.set(`team`,a.slackTeam.id),{ref:o,workspace:a.slackTeam.name==null?{workspaceUrl:s.href}:{workspaceUrl:s.href,workspaceName:a.slackTeam.name}}}function parseCreatedSlackConnector(e){let t=e.trim();if(!t)return;let n;try{n=JSON.parse(t)}catch{return}return parseSlackConnectorDetails(n)?.ref}function pickSlackConnector(e,t,n){if(t===void 0||typeof e!=`object`||!e)return;let r=e,i=r.connectors??r.clients;if(!Array.isArray(i))return;let a;for(let e of i){if(e.type!==`slack`||typeof e.uid!=`string`||typeof e.id!=`string`||!Array.isArray(e.projects)||!e.projects.some(e=>typeof e==`object`&&!!e&&e.id===t))continue;let r={uid:e.uid,id:e.id};if(n!==void 0&&r.uid===n)return r;let i=typeof e.createdAt==`number`?e.createdAt:0;(!a||i>a.createdAt)&&(a={ref:r,createdAt:i})}return a?.ref}function slackMessageDeepLink(e){let t=URL.parse(e);return t===null||!t.pathname.endsWith(`/app_redirect`)||!t.searchParams.has(`app`)||!t.searchParams.has(`team`)?e:(t.searchParams.set(`tab`,`messages`),t.href)}function parseSlackConnectors(e){if(typeof e!=`object`||!e)return[];let t=e,n=t.connectors??t.clients;if(!Array.isArray(n))return[];let r=[];for(let e of n){if(e.type!==`slack`||typeof e.uid!=`string`)continue;let t=Array.isArray(e.projects)?e.projects.map(e=>typeof e==`object`&&e?e.id:void 0).filter(e=>typeof e==`string`):[];r.push({uid:e.uid,projectIds:t})}return r}export{parseCreatedSlackConnector,parseSlackConnectorDetails,parseSlackConnectors,pickSlackConnector,slackMessageDeepLink};
@@ -1,15 +1,14 @@
1
1
  import { type ChannelSetupAwaitChoice, type ChannelSetupLog } from "#setup/cli/index.js";
2
- import { runVercelCaptureStdout } from "#setup/primitives/run-vercel.js";
3
- import { readProjectId, type SlackConnectLifecycleDeps } from "./slack-connect-lifecycle.js";
4
- export { parseCreatedSlackConnector, parseInstallation, pickSlackConnector, type SlackConnectorRef, } from "./slack-connect.js";
2
+ import { type SlackConnectorCreateDeps } from "./slack-connect-create.js";
3
+ import { readProjectLink, type SlackConnectLifecycleDeps } from "./slack-connect-lifecycle.js";
4
+ export { parseCreatedSlackConnector, parseSlackConnectorDetails, pickSlackConnector, type SlackConnectorRef, } from "./slack-connect.js";
5
5
  /** Injected for tests; defaults to the real Vercel CLI subprocess primitives. */
6
- export interface SlackbotProvisionDeps extends SlackConnectLifecycleDeps {
7
- runVercelCaptureStdout: typeof runVercelCaptureStdout;
8
- /** Test seam for the linked-project lookup. */
9
- readProjectId?: typeof readProjectId;
10
- /** Test seam for the installation poll's pacing; defaults to a real sleep. */
6
+ export interface SlackbotProvisionDeps extends SlackConnectLifecycleDeps, SlackConnectorCreateDeps {
7
+ /** Test seam for the linked Vercel project and team lookup. */
8
+ readProjectLink?: typeof readProjectLink;
9
+ /** Test seam for the workspace poll's pacing; defaults to a real sleep. */
11
10
  delay?: (ms: number, signal?: AbortSignal) => Promise<void>;
12
- /** Monotonic-enough clock for enforcing the installation deadline. */
11
+ /** Monotonic-enough clock for enforcing the workspace lookup deadline. */
13
12
  now?: () => number;
14
13
  }
15
14
  /**
@@ -54,13 +53,10 @@ export type ProvisionSlackbotResult = {
54
53
  workspaceName?: string;
55
54
  };
56
55
  /**
57
- * Creates a Slack Connect client, replaces its default trigger destination with the Eve route,
58
- * and waits for the app's Slack workspace installation. Promptless: progress streams
59
- * through the rail-styled `log`, and the result union carries every completed provisioning
60
- * outcome for the caller to act on. `attached` is only returned once an installation exists. An
61
- * attempt that times out is abandoned: its connector is removed and the result
62
- * is `not-installed`, so callers never treat an unfinished browser OAuth as a
63
- * working Slack connection — and no orphaned connector is left behind.
56
+ * Creates or reuses a Slack connector and points its event destination at Eve.
57
+ * A successful `connect create` is the completion boundary for a new browser
58
+ * flow. Existing connectors are verified through their team-scoped detail
59
+ * payload before attachment.
64
60
  */
65
61
  export interface ProvisionSlackbotOptions {
66
62
  /**
@@ -1 +1 @@
1
- import{parseCreatedSlackConnector,parseInstallation,pickSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,attachSlackConnector,cleanupCreatedAttempt,fetchInstallationInfo,findSlackConnector,listSlackConnectors,readProjectId}from"./slack-connect-lifecycle.js";import{join}from"node:path";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#setup/scaffold/index.js";import{setTimeout}from"node:timers/promises";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{performance}from"node:perf_hooks";import{updateSlackChannelConnectorUid}from"#setup/scaffold/update/update-slack-channel.js";const defaultDeps={captureVercel,runVercel,runVercelCaptureStdout},realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollInstallation(e,t,n,i,a){let s=e.delay??realDelay,c=e.now??monotonicNow,l=c()+3e5;for(;;){a?.throwIfAborted();let u=l-c();if(u<=0)return{state:`timed-out`};let d=await fetchInstallationInfo(e,t,n,i,Math.min(CONNECT_LOOKUP_TIMEOUT_MS,u),a);if(a?.throwIfAborted(),d.state!==`pending`)return d;let f=l-c();if(f<=0)return{state:`timed-out`};await s(Math.min(3e3,f),a)}}function isAbortFromSignal(e,t){return t?.aborted===!0&&(e===t.reason||e instanceof Error&&e.name===`AbortError`)}function cleanupFailureResult(e){return{state:`cleanup-failed`,connectorUids:e.connectorUids}}async function runAttempt(t){let{log:n,deps:r,projectRoot:a,onOutput:o,signal:s,phase:c}=t,l;if(t.source.state===`existing`)l=t.source.ref;else{let i=await c(`Creating a Slackbot through Vercel Connect...`,()=>r.runVercelCaptureStdout([`connect`,`create`,`slack`,`--triggers`,`--name`,t.slug,`-F`,`json`],{cwd:a,nonInteractive:!0,onOutput:o,timeoutMs:6e5,signal:s})),u=parseCreatedSlackConnector(i.stdout);if(u&&t.onCreated(u),!i.ok)return s?.throwIfAborted(),{state:`create-failed`};if(s?.throwIfAborted(),!u)return n.warning(`Vercel did not return an exact Slack connector UID for this request, so Eve cannot attach or remove it safely.`),{state:`unresolved`};l=u}let u=await c(`Configuring Slack event delivery for this agent...`,()=>attachSlackConnector(r,a,l,o,s));if(s?.throwIfAborted(),u.state!==`attached`)return{state:u.state,ref:l};let d=await c(`Waiting for the Slack workspace connection...`,()=>pollInstallation(r,a,l.id,o,s));return d.state===`installed`?{state:`installed`,ref:l,info:d.info}:d.state===`timed-out`?{state:`timed-out`,ref:l}:{state:`failed`,ref:l,message:d.message}}async function raceAttemptAgainstChoice(e){let t=new AbortController,n=e.outerSignal?AbortSignal.any([e.outerSignal,t.signal]):t.signal,r=e.run(n);try{let i=await Promise.race([r.then(e=>({via:`work`,outcome:e})),e.prompt.choice.then(e=>({via:`choice`,choice:e}))]);if(i.via===`work`)return i;t.abort();let a;try{a=await r}catch(e){if(!isAbortFromSignal(e,n))throw e}return{via:`choice`,choice:i.choice,settled:a}}catch(e){t.abort();try{await r}catch{}throw e}finally{e.prompt.close()}}async function provisionSlackbot(e,t,n,r=defaultDeps,i={}){i.signal?.throwIfAborted();let o=createPromptCommandOutput(e),u={log:e,deps:r,projectRoot:t,onOutput:o},f=await(r.readProjectId??readProjectId)(t),p=`slack/${n}`,m=await withPhase(e,`Checking for an existing Slackbot...`,()=>findSlackConnector(r,t,f,p,o,i.signal));if(i.signal?.throwIfAborted(),m.state===`failed`)return e.warning(`Could not inspect existing Slack connectors, so Eve did not create another one. ${m.message}`),{state:`connector-lookup-failed`};if(f===void 0&&m.connectorUids.size>0)return e.warning("Could not verify which Slack connectors belong to this Vercel project, so Eve did not create another one. Restore `.vercel/project.json`, then try again."),{state:`connector-lookup-failed`};let finishOutcome=async(t,n,r)=>{let i=n.state===`new`;switch(t.state){case`create-failed`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`unresolved`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`detach-failed`:return e.warning(`Could not remove the connector's existing trigger destination. Run \`vercel connect detach ${t.ref.uid} --yes\`, then \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\`.`),{state:`detach-failed`,connectorUid:t.ref.uid};case`attach-failed`:return e.warning(`Could not register this project as a trigger destination. Run \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\` to enable event delivery.`),{state:`attach-failed`,connectorUid:t.ref.uid};case`installed`:return{state:`attached`,connectorUid:t.ref.uid,chatUrl:t.info.workspaceUrl,workspaceName:t.info.workspaceName};case`failed`:return e.warning(`Could not verify the Slack workspace installation. ${t.message}`),{state:`installation-check-failed`,connectorUid:t.ref.uid};case`timed-out`:if(e.warning(`The Slackbot did not connect to a Slack workspace in time.`),i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`not-installed`}}},cleanupNewAttempt=async(e,t)=>cleanupCreatedAttempt(u,{expectedUid:p,baselineConnectorUids:e.baselineConnectorUids,createdRef:t});function attemptInput(i,a){return{log:e,deps:r,projectRoot:t,slug:n,source:i,onOutput:o,onCreated:a}}async function runExistingConnector(t){let cleanupCurrentAttempt=async()=>({state:`clean`}),notInstalled=()=>(e.warning(`The existing Slack connector \`${t.ref.uid}\` has no Slack workspace installation. Eve did not remove it because this run did not create it. If its original browser request is still open, complete it; otherwise run \`vercel connect remove ${t.ref.uid} --disconnect-all --yes\` before trying again.`),{state:`existing-not-installed`,connectorUid:t.ref.uid}),finishExistingOutcome=async e=>e.state===`timed-out`?notInstalled():finishOutcome(e,t,cleanupCurrentAttempt);if(i.awaitChoice!==void 0){let e=await raceAttemptAgainstChoice({prompt:i.awaitChoice({status:`Waiting for the existing Slack workspace connection...`,context:`Complete the original setup in the browser`,actions:[{value:`cancel`,label:`Stop waiting`}]}),outerSignal:i.signal,run:e=>runAttempt({...attemptInput(t,()=>{}),signal:e,phase:(e,t)=>t()})});return e.via===`work`?finishExistingOutcome(e.outcome):e.settled?.state===`installed`?finishExistingOutcome(e.settled):notInstalled()}return finishExistingOutcome(await runAttempt({...attemptInput(t,()=>{}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)}))}async function runUncontrolledAttempt(t){let n,cleanupCurrentAttempt=()=>cleanupNewAttempt(t,n),r=runAttempt({...attemptInput(t,e=>{n=e}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)});try{return await finishOutcome(await r,t,cleanupCurrentAttempt)}catch(e){throw i.signal?.aborted===!0&&await cleanupCurrentAttempt(),e}}async function runInteractiveAttempt(n,a){let s,cleanupCurrentAttempt=()=>cleanupNewAttempt(n,s),l=a({status:`Creating a Slackbot through Vercel Connect...`,context:`Waiting for you to complete setup in the browser`,actions:[{value:`retry`,label:`Did your browser not open? Try again`},{value:`cancel`,label:`Cancel`}]}),u;try{u=await raceAttemptAgainstChoice({prompt:l,outerSignal:i.signal,run:e=>runAttempt({...attemptInput(n,e=>{s=e}),signal:e,phase:(e,t)=>t()})})}catch(e){throw await cleanupCurrentAttempt(),e}if(u.via===`work`)return{state:`finished`,result:await finishOutcome(u.outcome,n,cleanupCurrentAttempt)};if(u.settled?.state===`installed`)return{state:`finished`,result:await finishOutcome(u.settled,n,cleanupCurrentAttempt)};let d=await cleanupCurrentAttempt();if(d.state===`failed`)return{state:`finished`,result:cleanupFailureResult(d)};if(u.choice===`retry`){let n=await withPhase(e,`Checking existing Slack connectors...`,()=>listSlackConnectors(r,t,o,i.signal));return i.signal?.throwIfAborted(),n.state===`failed`?(e.warning(`Could not inspect existing Slack connectors, so Eve did not create another one. ${n.message}`),{state:`finished`,result:{state:`connector-lookup-failed`}}):{state:`retry`,source:{state:`new`,baselineConnectorUids:new Set(n.connectors.map(e=>e.uid))}}}return{state:`finished`,result:{state:`cancelled`}}}if(m.state===`found`)return runExistingConnector({state:`existing`,ref:m.connector});let h={state:`new`,baselineConnectorUids:m.connectorUids};if(i.awaitChoice===void 0)return runUncontrolledAttempt(h);for(;;){let e=await runInteractiveAttempt(h,i.awaitChoice);if(e.state===`finished`)return e.result;h=e.source}}async function reconcileSlackUid(e,t,n,r){if(n.state!==`attached`||n.connectorUid===r)return!0;let{patched:i}=await updateSlackChannelConnectorUid(join(t,`agent/channels/slack.ts`),n.connectorUid);return i?!0:(e.warning(`Could not patch agent/channels/slack.ts automatically. Update \`connectSlackCredentials("...")\` to \`"${n.connectorUid}"\` and run \`vercel deploy --prod\`.`),!1)}export{parseCreatedSlackConnector,parseInstallation,pickSlackConnector,provisionSlackbot,reconcileSlackUid};
1
+ import{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,attachSlackConnector,cleanupCreatedAttempt,fetchSlackWorkspace,findSlackConnector,listSlackConnectors,readProjectLink}from"./slack-connect-lifecycle.js";import{createSlackConnector}from"./slack-connect-create.js";import{join}from"node:path";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#setup/scaffold/index.js";import{setTimeout}from"node:timers/promises";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{performance}from"node:perf_hooks";import{updateSlackChannelConnectorUid}from"#setup/scaffold/update/update-slack-channel.js";const defaultDeps={captureVercel,runVercel,runVercelCaptureStdout},realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollSlackWorkspace(e,t,n,i,a,s){let c=e.delay??realDelay,l=e.now??monotonicNow,u=l()+3e5;for(;;){s?.throwIfAborted();let d=u-l();if(d<=0)return{state:`timed-out`};let f=await fetchSlackWorkspace({deps:e,projectRoot:t,connectorId:n,orgId:i,onOutput:a,timeoutMs:Math.min(CONNECT_LOOKUP_TIMEOUT_MS,d),signal:s});if(s?.throwIfAborted(),f.state!==`pending`)return f;let p=u-l();if(p<=0)return{state:`timed-out`};await c(Math.min(3e3,p),s)}}function isAbortFromSignal(e,t){return t?.aborted===!0&&(e===t.reason||e instanceof Error&&e.name===`AbortError`)}function cleanupFailureResult(e){return{state:`cleanup-failed`,connectorUids:e.connectorUids}}async function runAttempt(e){let{log:t,deps:n,projectRoot:r,orgId:a,onOutput:o,signal:s,phase:c}=e,l,d;if(e.source.state===`existing`)l=e.source.ref;else{let i=await createSlackConnector({deps:n,projectRoot:r,orgId:a,slug:e.slug,onOutput:o,signal:s,phase:c,onCreated:e.onCreated,waitForWorkspace:async(e,t)=>{let i=await pollSlackWorkspace(n,r,e.id,a,o,t);return i.state===`connected`?i.workspace:void 0}});if(i.state===`failed`)return{state:`create-failed`};if(i.state===`unresolved`)return t.warning(`Vercel did not return an exact Slack connector UID for this request, so Eve cannot attach or remove it safely.`),{state:`unresolved`};l=i.ref,i.via===`workspace`&&(d=i.workspace)}if(e.source.state===`existing`&&d===void 0){let e=await c(`Waiting for the Slack workspace connection...`,()=>pollSlackWorkspace(n,r,l.id,a,o,s));if(e.state===`timed-out`)return{state:`timed-out`,ref:l};if(e.state===`failed`)return{state:`failed`,ref:l,message:e.message};d=e.workspace}let f=await c(`Configuring Slack event delivery for this agent...`,()=>attachSlackConnector(n,r,l,o,s));return s?.throwIfAborted(),f.state===`attached`?d===void 0?{state:`attached`,ref:l}:{state:`attached`,ref:l,workspace:d}:{state:f.state,ref:l}}async function raceAttemptAgainstChoice(e){let t=new AbortController,n=e.outerSignal?AbortSignal.any([e.outerSignal,t.signal]):t.signal,r=e.run(n);try{let i=await Promise.race([r.then(e=>({via:`work`,outcome:e})),e.prompt.choice.then(e=>({via:`choice`,choice:e}))]);if(i.via===`work`)return i;t.abort();let a;try{a=await r}catch(e){if(!isAbortFromSignal(e,n))throw e}return{via:`choice`,choice:i.choice,settled:a}}catch(e){t.abort();try{await r}catch{}throw e}finally{e.prompt.close()}}async function provisionSlackbot(e,t,n,r=defaultDeps,i={}){i.signal?.throwIfAborted();let o=createPromptCommandOutput(e),u={log:e,deps:r,projectRoot:t,onOutput:o},d=await(r.readProjectLink??readProjectLink)(t),p=d?.projectId,m=d?.orgId,h=`slack/${n}`,g=await withPhase(e,`Checking for an existing Slackbot...`,()=>findSlackConnector(r,t,p,h,o,i.signal));if(i.signal?.throwIfAborted(),g.state===`failed`)return e.warning(`Could not inspect existing Slack connectors, so Eve did not create another one. ${g.message}`),{state:`connector-lookup-failed`};if(p===void 0&&g.connectorUids.size>0)return e.warning("Could not verify which Slack connectors belong to this Vercel project, so Eve did not create another one. Restore `.vercel/project.json`, then try again."),{state:`connector-lookup-failed`};let finishOutcome=async(t,n,r)=>{let i=n.state===`new`;switch(t.state){case`create-failed`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`unresolved`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`detach-failed`:return e.warning(`Could not remove the connector's existing trigger destination. Run \`vercel connect detach ${t.ref.uid} --yes\`, then \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\`.`),{state:`detach-failed`,connectorUid:t.ref.uid};case`attach-failed`:return e.warning(`Could not register this project as a trigger destination. Run \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\` to enable event delivery.`),{state:`attach-failed`,connectorUid:t.ref.uid};case`attached`:return t.workspace===void 0?{state:`attached`,connectorUid:t.ref.uid}:{state:`attached`,connectorUid:t.ref.uid,chatUrl:t.workspace.workspaceUrl,workspaceName:t.workspace.workspaceName};case`failed`:return e.warning(`Could not verify the Slack workspace connection. ${t.message}`),{state:`installation-check-failed`,connectorUid:t.ref.uid};case`timed-out`:if(e.warning(`The Slackbot did not connect to a Slack workspace in time.`),i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`not-installed`}}},cleanupNewAttempt=async(e,t)=>cleanupCreatedAttempt(u,{expectedUid:h,baselineConnectorUids:e.baselineConnectorUids,createdRef:t});function attemptInput(i,a){return{log:e,deps:r,projectRoot:t,orgId:m,slug:n,source:i,onOutput:o,onCreated:a}}async function runExistingConnector(t){let cleanupCurrentAttempt=async()=>({state:`clean`}),notInstalled=()=>(e.warning(`The existing Slack connector \`${t.ref.uid}\` is not connected to a Slack workspace. Eve did not remove it because this run did not create it. If its original browser request is still open, complete it; otherwise run \`vercel connect remove ${t.ref.uid} --disconnect-all --yes\` before trying again.`),{state:`existing-not-installed`,connectorUid:t.ref.uid}),finishExistingOutcome=async e=>e.state===`timed-out`?notInstalled():finishOutcome(e,t,cleanupCurrentAttempt);if(i.awaitChoice!==void 0){let e=await raceAttemptAgainstChoice({prompt:i.awaitChoice({status:`Waiting for the existing Slack workspace connection...`,context:`Complete the original setup in the browser`,actions:[{value:`cancel`,label:`Stop waiting`}]}),outerSignal:i.signal,run:e=>runAttempt({...attemptInput(t,()=>{}),signal:e,phase:(e,t)=>t()})});return e.via===`work`?finishExistingOutcome(e.outcome):e.settled?.state===`attached`?finishExistingOutcome(e.settled):notInstalled()}return finishExistingOutcome(await runAttempt({...attemptInput(t,()=>{}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)}))}async function runUncontrolledAttempt(t){let n,cleanupCurrentAttempt=()=>cleanupNewAttempt(t,n),r=runAttempt({...attemptInput(t,e=>{n=e}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)});try{return await finishOutcome(await r,t,cleanupCurrentAttempt)}catch(e){throw i.signal?.aborted===!0&&await cleanupCurrentAttempt(),e}}async function runInteractiveAttempt(n,a){let s,cleanupCurrentAttempt=()=>cleanupNewAttempt(n,s),l=a({status:`Waiting for Slack setup to finish...`,context:`Complete setup in the browser, then wait while Eve verifies the connection`,actions:[{value:`retry`,label:`Did your browser not open? Try again`},{value:`cancel`,label:`Cancel`}]}),u;try{u=await raceAttemptAgainstChoice({prompt:l,outerSignal:i.signal,run:t=>runAttempt({...attemptInput(n,e=>{s=e}),signal:t,phase:e.spinner===void 0?(e,t)=>t():(t,n)=>withPhase(e,t,n)})})}catch(e){throw await cleanupCurrentAttempt(),e}if(u.via===`work`)return{state:`finished`,result:await finishOutcome(u.outcome,n,cleanupCurrentAttempt)};if(u.settled?.state===`attached`)return{state:`finished`,result:await finishOutcome(u.settled,n,cleanupCurrentAttempt)};let d=await cleanupCurrentAttempt();if(d.state===`failed`)return{state:`finished`,result:cleanupFailureResult(d)};if(u.choice===`retry`){let n=await withPhase(e,`Checking existing Slack connectors...`,()=>listSlackConnectors(r,t,o,i.signal));return i.signal?.throwIfAborted(),n.state===`failed`?(e.warning(`Could not inspect existing Slack connectors, so Eve did not create another one. ${n.message}`),{state:`finished`,result:{state:`connector-lookup-failed`}}):{state:`retry`,source:{state:`new`,baselineConnectorUids:new Set(n.connectors.map(e=>e.uid))}}}return{state:`finished`,result:{state:`cancelled`}}}if(g.state===`found`)return runExistingConnector({state:`existing`,ref:g.connector});let _={state:`new`,baselineConnectorUids:g.connectorUids};if(i.awaitChoice===void 0)return runUncontrolledAttempt(_);for(;;){let e=await runInteractiveAttempt(_,i.awaitChoice);if(e.state===`finished`)return e.result;_=e.source}}async function reconcileSlackUid(e,t,n,r){if(n.state!==`attached`||n.connectorUid===r)return!0;let{patched:i}=await updateSlackChannelConnectorUid(join(t,`agent/channels/slack.ts`),n.connectorUid);return i?!0:(e.warning(`Could not patch agent/channels/slack.ts automatically. Update \`connectSlackCredentials("...")\` to \`"${n.connectorUid}"\` and run \`vercel deploy --prod\`.`),!1)}export{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector,provisionSlackbot,reconcileSlackUid};
@@ -50,6 +50,31 @@ export declare function assertNewProjectNameAvailable(projectRoot: string, team:
50
50
  * API error and the user is already authenticated.
51
51
  */
52
52
  export declare function requireVercelLogin(failure?: VercelCaptureFailure): never;
53
+ /**
54
+ * Throws the right outcome for a failed `vercel whoami`. ENOENT means the
55
+ * binary isn't installed (its own action, not a login that would fail
56
+ * identically); the explicit not-authenticated diagnostic is a login action;
57
+ * anything else is a transient fault surfaced as a plain error, so the caller
58
+ * reports "try again" rather than mislabeling it "log in".
59
+ */
60
+ export declare function requireVercelAuth(failure: VercelCaptureFailure): never;
61
+ /**
62
+ * The Vercel auth state, read-only. `cli-missing` (ENOENT) and `unavailable` (a
63
+ * transient network/API fault) are each kept distinct from `logged-out` so a
64
+ * caller never tells the user to log in when the real cause is a missing CLI or
65
+ * an unreachable network. `logged-out` is claimed only from the explicit
66
+ * not-authenticated diagnostic.
67
+ */
68
+ export type VercelAuthStatus = "authenticated" | "logged-out" | "cli-missing" | "unavailable";
69
+ export declare function getVercelAuthStatus(projectRoot: string, options?: VercelProjectOperationOptions): Promise<VercelAuthStatus>;
70
+ /**
71
+ * Throws the re-auth action for a forbidden scope. The session is logged in,
72
+ * but a scoped command was denied — the SSO/SAML or non-membership case. The
73
+ * remedy is still `vercel login` (it completes the team's SSO and refreshes the
74
+ * token), so the dev TUI routes it to `/login`; the reason names the membership
75
+ * fallback for the case re-auth cannot fix.
76
+ */
77
+ export declare function requireVercelTeamAccess(failure: VercelCaptureFailure): never;
53
78
  /**
54
79
  * Ensures Vercel authentication before any provisioning. `vercel whoami` exits
55
80
  * non-zero when not logged in; any other failure (a missing CLI, a transient
@@ -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{boolean,object,record,string,unknown}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";import{captureVercel,runVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{whimsyFor}from"#setup/cli/index.js";var import_picocolors=__toESM(require_picocolors(),1);const JsonObjectSchema=record(string(),unknown()),VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelProjectReferenceSchema=object({id:string(),name:string()}),VercelApiErrorSchema=object({error:object({code:string().optional(),message:string().optional()}).optional()});function unresolvedProject(){return{kind:`unresolved`}}async function withNetworkSpinner(e,t,n){let r=e.log.spinner?.(t);try{return await n()}finally{r?.stop()}}function projectIdFromResolution(e){return e.kind===`unresolved`?void 0:e.projectId}function parseJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}function parseVercelJsonList(e,t,n){let r=parseJson(e,t),i=JsonObjectSchema.safeParse(r);if(!i.success)throw Error(`Could not read ${t} from Vercel CLI JSON output.`);let a=i.data[t];if(!Array.isArray(a))throw Error(`Vercel CLI JSON output did not include ${t}.`);let o=[];for(let e of a){let t=n.safeParse(e);t.success&&o.push(t.data)}return o}function parseTeamList(e){return e===void 0?[]:parseVercelJsonList(e,`teams`,VercelTeamListEntrySchema)}async function listTeams(e,t={}){let n=await captureVercel([`teams`,`ls`,`--format`,`json`],{cwd:e,signal:t.signal});if(!n.ok)throw Error(`Could not list Vercel teams. ${n.failure.message}`);return parseTeamList(n.stdout)}function parseProjectList(e){return e===void 0?[]:parseVercelJsonList(e,`projects`,VercelProjectListEntrySchema)}async function listProjects(e,t,n={}){let r=await captureVercel([`project`,`ls`,`--format`,`json`,`--scope`,t],{cwd:e,signal:n.signal});if(!r.ok)throw Error(`Could not list Vercel projects in ${t}. ${r.failure.message}`);return parseProjectList(r.stdout)}function isNotFoundApiFailure(e){if(e.code===404)return!0;let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stderr}`.toLowerCase();return r.includes(`not_found`)||r.includes(`not found`)||r.includes(`404`)}function isConflictApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`409`)||r.includes(`conflict`)||r.includes(`already exists`)}function safeParseJson(e){try{return JSON.parse(e)}catch{return}}function parseProjectReference(e,t){let n=VercelProjectReferenceSchema.safeParse(parseJson(e,t));if(!n.success)throw Error(`Could not read Vercel project identity from ${t}.`);return n.data}async function resolveProjectByNameOrId(e,t,n,r={}){let i=await captureVercel([`api`,`/v9/projects/${encodeURIComponent(n)}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal});if(i.ok)return parseProjectReference(i.stdout,`project ${n}`);if(isNotFoundApiFailure(i.failure))return null;throw Error(`Could not resolve project "${n}" in ${t}. ${i.failure.message}`)}async function createProject(e,t,n,r,i){let a=await captureVercel([`api`,`/v10/projects`,`--scope`,t,`--method`,`POST`,`--raw-field`,`name=${n}`,`--raw`],{cwd:e,onOutput:r,signal:i.signal});if(a.ok)return parseProjectReference(a.stdout,`created project ${n}`);throw isConflictApiFailure(a.failure)?Error(projectNameCollisionMessage(n,t)):Error(`Could not create Vercel project "${n}" in ${t}. ${a.failure.message}`)}function projectNameCollisionMessage(e,t){return`Vercel project "${e}" already exists in ${t}. Pass --project ${e} to link it, or choose a different project name.`}async function assertNewProjectNameAvailable(e,t,n,r={}){if(await resolveProjectByNameOrId(e,t,n,r)!==null)throw Error(projectNameCollisionMessage(n,t))}function requireVercelLogin(e){let t=`Provisioning a Vercel project requires you to be logged in to Vercel.`,n=e?.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-login`,command:`vercel login`,reason:e?`${t} The Vercel CLI check did not succeed: ${e.message}${n?` ${n}`:``}`:t})}async function requireAuth(e,t,n={}){let check=async()=>{let t=await captureVercel([`whoami`],{cwd:e,signal:n.signal});n.signal?.throwIfAborted(),t.ok||requireVercelLogin(t.failure)};if(t===void 0){await check();return}await withNetworkSpinner(t,whimsyFor(`auth`),check)}async function isVercelAuthenticated(e,t={}){let n=await captureVercel([`whoami`],{cwd:e,signal:t.signal});return t.signal?.throwIfAborted(),n.ok}async function whoamiScope(e,t){let n=await captureVercel([`whoami`],{cwd:e,signal:t.signal});return t.signal?.throwIfAborted(),n.ok||requireVercelLogin(n.failure),n.stdout.trim()}async function resolveTeam(e,t,n={}){return t===void 0?(await listTeams(e,n)).find(e=>e.current)?.slug??await whoamiScope(e,n):t}async function validateTeam(e,t,n,r={}){if(n===void 0)return;let i=await listTeams(t,r);if(i.length>0&&!i.some(e=>e.slug===n))throw Error(`Team "${n}" was not found in \`vercel teams ls\`. Pass a valid team slug or omit --team.`)}async function pickTeam(e,t,n,r={}){if(n!==void 0)return await validateTeam(e,t,n,r),resolveTeam(t,n,r);let i=await withNetworkSpinner(e,whimsyFor(`teams`),()=>listTeams(t,r));return i.length<=1?i.find(e=>e.current)?.slug??await whoamiScope(t,r):e.select({message:`Select your team`,search:!0,placeholder:`type to search teams`,options:i.map(e=>({value:e.slug,label:e.current?`${e.name} (current)`:e.name})),initialValue:i.find(e=>e.current)?.slug})}async function pickProject(e,t,n,r={}){let i=await withNetworkSpinner(e,whimsyFor(`projects`,n),()=>listProjects(t,n,r));if(i.length===0){if(r.allowCreateWhenEmpty===!1)throw Error(`No existing Vercel projects found in ${n}. Create one in Vercel, then try again.`);return{project:await e.text({message:`No projects found in ${n}. Enter a project name to create`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}),exists:!1}}return{project:await e.select({message:`Project to link`,search:!0,placeholder:`type to filter projects`,options:i.map(e=>({value:e.name,label:e.name}))}),exists:!0}}async function pickNewProjectName(e,t,n,r,i={}){let a=await withNetworkSpinner(e,whimsyFor(`project-name`,n),()=>resolveProjectByNameOrId(t,n,r.trim(),i)),o=r.trim();for(;a!==null;)o=(await e.text({message:`New project name`,defaultValue:`${o}-2`,notices:[{tone:`warning`,text:`${import_picocolors.default.yellow(`Project named`)} '${import_picocolors.default.blue(o)}' ${import_picocolors.default.yellow(`already exists in`)} '${import_picocolors.default.blue(n)}'`}],validate:e=>{if(e.trim().length===0)return`Project name cannot be empty.`}})).trim(),a=await resolveProjectByNameOrId(t,n,o,i);return o}async function linkProject(e,t,n,r,i={}){let a=[`--scope`,n.team],o;if(n.kind===`new`)o=await withNetworkSpinner(e,`Creating Vercel project "${n.project}" in ${n.team}...`,async()=>(await assertNewProjectNameAvailable(t,n.team,n.project,i),createProject(t,n.team,n.project,r,i)));else{let e=await resolveProjectByNameOrId(t,n.team,n.project,i);if(e===null)throw Error(`Vercel project "${n.project}" was not found in ${n.team}.`);o=e}return withNetworkSpinner(e,`Linking this directory to Vercel project "${o.name}"...`,()=>runVercel([`link`,`--project`,o.id,...a,`--yes`],{cwd:t,onOutput:r,signal:i.signal}))}export{assertNewProjectNameAvailable,isVercelAuthenticated,linkProject,listProjects,listTeams,pickNewProjectName,pickProject,pickTeam,projectIdFromResolution,requireAuth,requireVercelLogin,resolveProjectByNameOrId,resolveTeam,unresolvedProject,validateTeam,withNetworkSpinner};
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{boolean,object,record,string,unknown}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";import{captureVercel,runVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{whimsyFor}from"#setup/cli/index.js";var import_picocolors=__toESM(require_picocolors(),1);const JsonObjectSchema=record(string(),unknown()),VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelProjectReferenceSchema=object({id:string(),name:string()}),VercelApiErrorSchema=object({error:object({code:string().optional(),message:string().optional()}).optional()});function unresolvedProject(){return{kind:`unresolved`}}async function withNetworkSpinner(e,t,n){let r=e.log.spinner?.(t);try{return await n()}finally{r?.stop()}}function projectIdFromResolution(e){return e.kind===`unresolved`?void 0:e.projectId}function parseJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}function parseVercelJsonList(e,t,n){let r=parseJson(e,t),i=JsonObjectSchema.safeParse(r);if(!i.success)throw Error(`Could not read ${t} from Vercel CLI JSON output.`);let a=i.data[t];if(!Array.isArray(a))throw Error(`Vercel CLI JSON output did not include ${t}.`);let o=[];for(let e of a){let t=n.safeParse(e);t.success&&o.push(t.data)}return o}function parseTeamList(e){return e===void 0?[]:parseVercelJsonList(e,`teams`,VercelTeamListEntrySchema)}async function listTeams(e,t={}){let n=await captureVercel([`teams`,`ls`,`--format`,`json`],{cwd:e,signal:t.signal});if(!n.ok)throw isForbiddenApiFailure(n.failure)&&requireVercelTeamAccess(n.failure),Error(`Could not list Vercel teams. ${n.failure.message}`);return parseTeamList(n.stdout)}function parseProjectList(e){return e===void 0?[]:parseVercelJsonList(e,`projects`,VercelProjectListEntrySchema)}async function listProjects(e,t,n={}){let r=await captureVercel([`project`,`ls`,`--format`,`json`,`--scope`,t],{cwd:e,signal:n.signal});if(!r.ok)throw isForbiddenApiFailure(r.failure)&&requireVercelTeamAccess(r.failure),Error(`Could not list Vercel projects in ${t}. ${r.failure.message}`);return parseProjectList(r.stdout)}function isNotFoundApiFailure(e){if(e.code===404)return!0;let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stderr}`.toLowerCase();return r.includes(`not_found`)||r.includes(`not found`)||r.includes(`404`)}function isConflictApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`409`)||r.includes(`conflict`)||r.includes(`already exists`)}function isForbiddenApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`403`)||r.includes(`forbidden`)||r.includes(`not_authorized`)||r.includes(`not authorized`)||r.includes(`sso`)||r.includes(`saml`)}function safeParseJson(e){try{return JSON.parse(e)}catch{return}}function parseProjectReference(e,t){let n=VercelProjectReferenceSchema.safeParse(parseJson(e,t));if(!n.success)throw Error(`Could not read Vercel project identity from ${t}.`);return n.data}async function resolveProjectByNameOrId(e,t,n,r={}){let i=await captureVercel([`api`,`/v9/projects/${encodeURIComponent(n)}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal});if(i.ok)return parseProjectReference(i.stdout,`project ${n}`);if(isNotFoundApiFailure(i.failure))return null;throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not resolve project "${n}" in ${t}. ${i.failure.message}`)}async function createProject(e,t,n,r,i){let a=await captureVercel([`api`,`/v10/projects`,`--scope`,t,`--method`,`POST`,`--raw-field`,`name=${n}`,`--raw`],{cwd:e,onOutput:r,signal:i.signal});if(a.ok)return parseProjectReference(a.stdout,`created project ${n}`);throw isConflictApiFailure(a.failure)?Error(projectNameCollisionMessage(n,t)):(isForbiddenApiFailure(a.failure)&&requireVercelTeamAccess(a.failure),Error(`Could not create Vercel project "${n}" in ${t}. ${a.failure.message}`))}function projectNameCollisionMessage(e,t){return`Vercel project "${e}" already exists in ${t}. Pass --project ${e} to link it, or choose a different project name.`}async function assertNewProjectNameAvailable(e,t,n,r={}){if(await resolveProjectByNameOrId(e,t,n,r)!==null)throw Error(projectNameCollisionMessage(n,t))}function requireVercelLogin(e){let t=`Provisioning a Vercel project requires you to be logged in to Vercel.`,n=e?.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-login`,command:`vercel login`,reason:e?`${t} The Vercel CLI check did not succeed: ${e.message}${n?` ${n}`:``}`:t})}function probeWhoami(e,t){return captureVercel([`whoami`],{cwd:e,signal:t.signal,timeoutMs:1e4})}function isLoggedOutFailure(e){let t=`${e.stdout} ${e.stderr}`.toLowerCase();return t.includes(`credentials`)||t.includes(`not authenticated`)||t.includes(`not logged in`)}function requireVercelAuth(e){if(e.errno===`ENOENT`)throw new HumanActionRequiredError({kind:`vercel-cli-missing`,command:`npm i -g vercel@latest`,reason:e.message});isLoggedOutFailure(e)&&requireVercelLogin(e);let t=e.stderr.trim();throw Error(`Couldn't verify your Vercel login: ${e.message}${t?` ${t}`:``}`)}async function getVercelAuthStatus(e,t={}){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok?`authenticated`:n.failure.errno===`ENOENT`?`cli-missing`:isLoggedOutFailure(n.failure)?`logged-out`:`unavailable`}function requireVercelTeamAccess(e){let t=e.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-forbidden`,command:`vercel login`,reason:`Vercel denied access to this scope.${t?` ${t}`:``} Re-authenticate (for example to complete a team's SSO) or switch to a team you can access.`})}async function requireAuth(e,t,n={}){let check=async()=>{let t=await probeWhoami(e,n);n.signal?.throwIfAborted(),t.ok||requireVercelAuth(t.failure)};if(t===void 0){await check();return}await withNetworkSpinner(t,whimsyFor(`auth`),check)}async function isVercelAuthenticated(e,t={}){return await getVercelAuthStatus(e,t)===`authenticated`}async function whoamiScope(e,t){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok||requireVercelAuth(n.failure),n.stdout.trim()}async function resolveTeam(e,t,n={}){return t===void 0?(await listTeams(e,n)).find(e=>e.current)?.slug??await whoamiScope(e,n):t}async function validateTeam(e,t,n,r={}){if(n===void 0)return;let i=await listTeams(t,r);if(i.length>0&&!i.some(e=>e.slug===n))throw Error(`Team "${n}" was not found in \`vercel teams ls\`. Pass a valid team slug or omit --team.`)}async function pickTeam(e,t,n,r={}){if(n!==void 0)return await validateTeam(e,t,n,r),resolveTeam(t,n,r);let i=await withNetworkSpinner(e,whimsyFor(`teams`),()=>listTeams(t,r));return i.length<=1?i.find(e=>e.current)?.slug??await whoamiScope(t,r):e.select({message:`Select your team`,search:!0,placeholder:`type to search teams`,options:i.map(e=>({value:e.slug,label:e.current?`${e.name} (current)`:e.name})),initialValue:i.find(e=>e.current)?.slug})}async function pickProject(e,t,n,r={}){let i=await withNetworkSpinner(e,whimsyFor(`projects`,n),()=>listProjects(t,n,r));if(i.length===0){if(r.allowCreateWhenEmpty===!1)throw Error(`No existing Vercel projects found in ${n}. Create one in Vercel, then try again.`);return{project:await e.text({message:`No projects found in ${n}. Enter a project name to create`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}),exists:!1}}return{project:await e.select({message:`Project to link`,search:!0,placeholder:`type to filter projects`,options:i.map(e=>({value:e.name,label:e.name}))}),exists:!0}}async function pickNewProjectName(e,t,n,r,i={}){let a=await withNetworkSpinner(e,whimsyFor(`project-name`,n),()=>resolveProjectByNameOrId(t,n,r.trim(),i)),o=r.trim();for(;a!==null;)o=(await e.text({message:`New project name`,defaultValue:`${o}-2`,notices:[{tone:`warning`,text:`${import_picocolors.default.yellow(`Project named`)} '${import_picocolors.default.blue(o)}' ${import_picocolors.default.yellow(`already exists in`)} '${import_picocolors.default.blue(n)}'`}],validate:e=>{if(e.trim().length===0)return`Project name cannot be empty.`}})).trim(),a=await resolveProjectByNameOrId(t,n,o,i);return o}async function linkProject(e,t,n,r,i={}){let a=[`--scope`,n.team],o;if(n.kind===`new`)o=await withNetworkSpinner(e,`Creating Vercel project "${n.project}" in ${n.team}...`,async()=>(await assertNewProjectNameAvailable(t,n.team,n.project,i),createProject(t,n.team,n.project,r,i)));else{let e=await resolveProjectByNameOrId(t,n.team,n.project,i);if(e===null)throw Error(`Vercel project "${n.project}" was not found in ${n.team}.`);o=e}return withNetworkSpinner(e,`Linking this directory to Vercel project "${o.name}"...`,()=>runVercel([`link`,`--project`,o.id,...a,`--yes`],{cwd:t,onOutput:r,signal:i.signal}))}export{assertNewProjectNameAvailable,getVercelAuthStatus,isVercelAuthenticated,linkProject,listProjects,listTeams,pickNewProjectName,pickProject,pickTeam,projectIdFromResolution,requireAuth,requireVercelAuth,requireVercelLogin,requireVercelTeamAccess,resolveProjectByNameOrId,resolveTeam,unresolvedProject,validateTeam,withNetworkSpinner};
@@ -16,10 +16,10 @@ export type SourceEdit = {
16
16
  * Rewrites the `model` string literal passed to `defineAgent({ ... })` in
17
17
  * `sourceText`, returning the edited source.
18
18
  *
19
- * Pure transform: parses with oxc, finds the literal's byte span, and splices
19
+ * Pure transform: parses with Rolldown, finds the literal's byte span, and splices
20
20
  * only those bytes, so comments, formatting, and quote style everywhere else
21
21
  * are preserved by construction. Bails (no edit) when `model` is absent or
22
22
  * isn't a plain string literal. An env reference, a template, an inlined SDK
23
23
  * model object, or a spread all opt out into the manual path instead.
24
24
  */
25
- export declare function applyModelNameToSource(sourceText: string, modelName: string): SourceEdit;
25
+ export declare function applyModelNameToSource(sourceText: string, modelName: string): Promise<SourceEdit>;
@@ -1,2 +1,2 @@
1
- import{parseSync}from"oxc-parser";const AGENT_FACTORY=`defineAgent`;function applyModelNameToSource(n,r){let i=parseSync(`agent.ts`,n,{lang:`ts`});if(i.errors.length>0){let e=i.errors[0];return{kind:`bail`,reason:`agent.ts does not parse: ${e?.message??`unknown parse error`}`,line:lineAt(n,e?.labels[0]?.start??0)}}let a=findDefineAgentObject(i.program,AGENT_FACTORY);if(a===void 0)return{kind:`bail`,reason:`no \`export default ${AGENT_FACTORY}({ ... })\` call found`,line:1};let o=findStringLiteralProperty(a,`model`);if(o===void 0)return{kind:`bail`,reason:"`model` is absent or is not a string literal (e.g. an env reference, a template, or an inlined SDK model)",line:lineAt(n,a.start)};let s=o.value;if(s===r)return{kind:`applied`,from:s,to:r,nextSource:n};let c=o.raw?.[0]===`'`?`'`:`"`,l=`${c}${escapeForQuote(r,c)}${c}`;return{kind:`applied`,from:s,to:r,nextSource:n.slice(0,o.start)+l+n.slice(o.end)}}function unwrapExpression(e){let t=e;for(;t.type===`ParenthesizedExpression`||t.type===`TSAsExpression`||t.type===`TSSatisfiesExpression`;)t=t.expression;return t}function findDefineAgentObject(e,t){for(let n of e.body){if(n.type!==`ExportDefaultDeclaration`)continue;let e=n.declaration;if(e.type!==`CallExpression`&&e.type!==`ParenthesizedExpression`&&e.type!==`TSAsExpression`&&e.type!==`TSSatisfiesExpression`)continue;let r=unwrapExpression(e);if(r.type!==`CallExpression`||!isFactoryCallee(r.callee,t))continue;let i=r.arguments[0];if(i===void 0||i.type===`SpreadElement`)continue;let a=unwrapExpression(i);if(a.type===`ObjectExpression`)return a}}function isFactoryCallee(e,t){return e.type===`Identifier`&&e.name===t}function findStringLiteralProperty(e,t){for(let n of e.properties){if(n.type!==`Property`||n.computed||!keyMatches(n.key,t))continue;let e=unwrapExpression(n.value);return e.type===`Literal`&&typeof e.value==`string`?e:void 0}}function keyMatches(e,t){return e.type===`Identifier`?e.name===t:e.type===`Literal`?typeof e.value==`string`&&e.value===t:!1}function escapeForQuote(e,t){return e.replaceAll(`\\`,`\\\\`).replaceAll(t,`\\${t}`)}function lineAt(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e[r]===`
1
+ import{parseWithNitroRolldownAst}from"#internal/bundler/nitro-rolldown.js";const AGENT_FACTORY=`defineAgent`;async function applyModelNameToSource(e,n){let r=await parseAgentSource(e);if(`kind`in r)return r;if((r.errors?.length??0)>0){let t=r.errors?.[0];return{kind:`bail`,reason:`agent.ts does not parse: ${t?.message??`unknown parse error`}`,line:parseErrorLine(e,t)}}let i=findDefineAgentObject(r.program??r,AGENT_FACTORY);if(i===void 0)return{kind:`bail`,reason:`no \`export default ${AGENT_FACTORY}({ ... })\` call found`,line:1};let a=findStringLiteralProperty(i,`model`);if(a===void 0)return{kind:`bail`,reason:"`model` is absent or is not a string literal (e.g. an env reference, a template, or an inlined SDK model)",line:lineAt(e,i.start??0)};let o=a.value;if(o===n)return{kind:`applied`,from:o,to:n,nextSource:e};let s=a.raw?.[0]===`'`?`'`:`"`,c=`${s}${escapeForQuote(n,s)}${s}`;return{kind:`applied`,from:o,to:n,nextSource:e.slice(0,a.start)+c+e.slice(a.end)}}async function parseAgentSource(t){try{return await parseWithNitroRolldownAst(`agent.ts`,t)}catch(e){let n=e;return{kind:`bail`,reason:`agent.ts does not parse: ${n.message??`unknown parse error`}`,line:parseErrorLine(t,n)}}}function unwrapExpression(e){let t=e;for(;t.type===`ParenthesizedExpression`||t.type===`TSAsExpression`||t.type===`TSSatisfiesExpression`;){if(t.expression===void 0||t.expression===null)return t;t=t.expression}return t}function findDefineAgentObject(e,t){for(let n of e.body??[]){if(n.type!==`ExportDefaultDeclaration`)continue;let e=n.declaration;if(e==null||e.type!==`CallExpression`&&e.type!==`ParenthesizedExpression`&&e.type!==`TSAsExpression`&&e.type!==`TSSatisfiesExpression`)continue;let r=unwrapExpression(e);if(r.type!==`CallExpression`||!isFactoryCallee(r.callee,t))continue;let i=r.arguments?.[0];if(i===void 0||i.type===`SpreadElement`)continue;let a=unwrapExpression(i);if(a.type===`ObjectExpression`)return a}}function isFactoryCallee(e,t){return e?.type===`Identifier`&&e.name===t}function findStringLiteralProperty(e,t){for(let n of e.properties){if(n.type!==`Property`||n.computed||!keyMatches(n.key,t))continue;let e=n.value;if(!isAstNode(e))continue;let r=unwrapExpression(e);return r.type===`Literal`&&typeof r.value==`string`&&r.start!==void 0&&r.end!==void 0?r:void 0}}function keyMatches(e,t){return e===void 0?!1:e.type===`Identifier`?e.name===t:e.type===`Literal`?typeof e.value==`string`&&e.value===t:!1}function isAstNode(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}function parseErrorLine(e,t){return typeof t?.loc?.line==`number`?t.loc.line:lineAt(e,t?.labels?.[0]?.start??t?.start??0)}function escapeForQuote(e,t){return e.replaceAll(`\\`,`\\\\`).replaceAll(t,`\\${t}`)}function lineAt(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e[r]===`
2
2
  `&&(n+=1);return n}export{applyModelNameToSource};
@@ -1 +1 @@
1
- import{join}from"node:path";import{readFile,rename,writeFile}from"node:fs/promises";import{applyModelNameToSource}from"#source-change/apply-model-name.js";function createStaticSourceChange(e){return{updateModelName:t=>updateAgentModelName(e,t)}}async function updateAgentModelName(n,r){let i=n.configModule;if(i===void 0)return{kind:`bail`,reason:`agent has no agent.ts config module to edit`,at:{logicalPath:`agent.ts`,line:1}};let a=join(n.agentRoot,i.logicalPath),o=await readFile(a,`utf8`),s=applyModelNameToSource(o,r);if(s.kind===`bail`)return{kind:`bail`,reason:s.reason,at:{logicalPath:i.logicalPath,line:s.line}};if(s.nextSource!==o){let e=`${a}.${process.pid}.eve-tmp`;await writeFile(e,s.nextSource,`utf8`),await rename(e,a)}return{kind:`applied`,from:s.from,to:s.to}}export{createStaticSourceChange};
1
+ import{join}from"node:path";import{readFile,rename,writeFile}from"node:fs/promises";import{applyModelNameToSource}from"#source-change/apply-model-name.js";function createStaticSourceChange(e){return{updateModelName:t=>updateAgentModelName(e,t)}}async function updateAgentModelName(n,r){let i=n.configModule;if(i===void 0)return{kind:`bail`,reason:`agent has no agent.ts config module to edit`,at:{logicalPath:`agent.ts`,line:1}};let a=join(n.agentRoot,i.logicalPath),o=await readFile(a,`utf8`),s=await applyModelNameToSource(o,r);if(s.kind===`bail`)return{kind:`bail`,reason:s.reason,at:{logicalPath:i.logicalPath,line:s.line}};if(s.nextSource!==o){let e=`${a}.${process.pid}.eve-tmp`;await writeFile(e,s.nextSource,`utf8`),await rename(e,a)}return{kind:`applied`,from:s.from,to:s.to}}export{createStaticSourceChange};
@@ -167,11 +167,11 @@ See [Sessions, runs & streaming](./concepts/sessions-runs-and-streaming) for the
167
167
 
168
168
  If a coding agent (Claude Code, Cursor, and the like) is doing the setup, hand it this prompt:
169
169
 
170
- <CopyPrompt text="Set up an Eve agent for the user. Eve is a filesystem-first TypeScript framework for durable agents, published as the npm package eve. Read its docs: once eve is installed they are bundled in the package at node_modules/eve/docs; before eve is installed, read the published Introduction and Getting Started pages. If the project has no Eve app, scaffold one with `npx eve@latest init <name>`; add `--channel-web-nextjs` only when the user wants Web Chat. The init command installs dependencies, initializes Git, and starts the dev server, so run it in a controllable process and stop it before editing. To add Eve to an existing app, run `npm install eve@latest`. Make sure agent/agent.ts and agent/instructions.md exist, then add a first typed tool at agent/tools/get_weather.ts using defineTool from eve/tools with a Zod inputSchema and an inline execute. Start the dev server again, then exercise the HTTP API: create a session with POST /eve/v1/session, attach to GET /eve/v1/session/:id/stream, and send a follow-up with the returned continuationToken. Verify with the project's typecheck, adapt model and provider choices to the project, and do not commit unless the user asks.">
171
- Set up an Eve agent: read the Eve docs (bundled at node_modules/eve/docs once eve is
172
- installed), scaffold with `npx eve@latest init <name>` (or `npm install eve@latest` in an existing app), add
173
- a typed tool at agent/tools/get_weather.ts, run it with `npm run dev`, then create a session, stream
174
- it, and send a follow-up.
170
+ <CopyPrompt text="Set up an Eve agent for the user. Ask one question at a time. Use your prompt tools when available and do not guess. First ask what the agent should do. Then ask whether to create a new project or add it to an existing directory. For a new project, propose a name and ask the user to confirm it, then ask whether it should include Web Chat. Run `npx eve@latest init <target>`, adding `--channel-web-nextjs` if the user wants Web Chat. After scaffolding, read the relevant guide in `<project>/node_modules/eve/docs/`, replace the placeholder in `<project>/agent/instructions.md` with the purpose the user gave you, and do not start `eve dev` because it is interactive. Give the user the exact dev command printed by init.">
171
+ Set up an Eve agent by asking what it should do, whether to create a new project or use an
172
+ existing directory, and whether a new project should include Web Chat, one question at a time.
173
+ Run `npx eve@latest init <target>`, read the bundled docs, fill in agent/instructions.md, and give
174
+ the user the dev command instead of starting the interactive server.
175
175
  </CopyPrompt>
176
176
 
177
177
  Once `eve` is a dependency, the full docs are bundled in the package, so the agent can read them locally without fetching anything:
@@ -20,7 +20,9 @@ If agent discovery reported problems, an error/warning count renders between the
20
20
 
21
21
  From there the conversation streams straight into your terminal's normal scrollback — your prompts, the agent's replies, reasoning, tool calls, nested subagents, connection-authorization prompts, and any captured `stdout`, `stderr`, or sandbox lifecycle lines — so you keep native scrolling, copy/paste, and a transcript that persists after you exit. Each turn is rendered without boxes: a colored gutter glyph marks who's speaking, tool calls collapse to a one-line summary (`✓ get_weather city="SF" → 73°F`), and a subagent's work is indented beneath its `◆` header. A sticky footer keeps you oriented. When input is ready, the prompt stays bare until you type; while a turn or setup action owns the terminal, only its live status is shown. Beneath the prompt or status, a persistent line shows the model, the session's token flow (`↑ 394.4K ↓ 4.3K`), the linked Vercel project and team (`▲ my-agent (acme)`), and a yellow `/deploy pending` marker once a channel added this session still needs `/deploy`. The Vercel segment stays hidden until the directory is linked. Press `Enter` to send; `Ctrl+C` interrupts a running turn or quits at the prompt. Slash commands: `/new` starts a fresh session and `/exit` quits.
22
22
 
23
- When `eve dev` runs the server locally, three more slash commands manage the project without leaving the session. Bare `/model` opens a configure menu that loops until Done (or Esc). "Change model" runs the same searchable model picker setup uses (the AI Gateway catalog, pre-selected on the model the runtime is serving); a model change is written into your agent's authored source, and the command reports success only after Eve confirms the new id (`/model <provider/model-id>` applies one directly, skipping the menu). The provider row opens the provider questions: which model provider to use (picking something other than AI Gateway shows wiring instructions for your own provider and stops there, leaving any existing setup untouched) and how to connect to AI Gateway — paste your own `AI_GATEWAY_API_KEY`, saved straight to `.env.local`, or connect via a project, which asks for a Vercel team and then opens that team's existing-project list (picking again re-links) before pulling the project's environment so an AI Gateway credential lands in `.env.local`; the dev server reloads env files automatically, no restart needed. The row demands attention (a bold yellow "Configure provider" with "Required to enable the agent") until a link or gateway credential is detected, then names the connection (e.g. "AI Gateway (Linked to my-project in my-team)") after, and each action's latest outcome stays visible beneath the menu (e.g. "✓ Model changed to openai/gpt-5.5"). `/channels` shows the agent's channel list — already-registered channels render as checked, focusable rows with an "Already installed" hint — and adds the one you pick, including the Slack Connect provisioning, then installs the dependencies the scaffold added so the dev server can load the new channels right away; after each addition the list repaints with the channel checked, until Done (or Esc) leaves the flow. `/deploy` ships the agent to Vercel production, linking first when the directory is unlinked. Each command echoes as an invocation line, asks through a bordered panel that takes the input area's place — one question at a time, clearly separate from the chat transcript — and finishes with a one-line `⎿` result; loading states stay on the ephemeral status line instead of piling into the transcript. These commands are not available when connected to a remote server with `--url`, and when a turn fails because AI Gateway authentication is missing or stale, the error points you at `/model` directly. The TUI also checks at startup: a missing model-provider setup surfaces as an attention line — `⚠ 1 setup issue: model provider not linked · /model` — so the fix is visible before the first message fails, and each command's outcome hangs under it with the `⎿` connector.
23
+ When `eve dev` runs the server locally, five more slash commands manage the project without leaving the session. `/vc` installs the Vercel CLI (a global install with your package manager) when it is missing — the fix command the "Vercel CLI not found" diagnostic points at. `/login` signs in to Vercel through the CLI's browser flow: the panel waits while you authenticate in the browser and Cancel stops waiting. These are the prerequisites the others route to — a logged-out `/model` link, `/channels` Slack provisioning, and `/deploy` all point you at `/login` (or `/vc` when the CLI itself is absent) instead of failing with a raw error, and a `403` from a team that enforces SSO routes to `/login` too (re-authenticating completes the team's SSO). Bare `/model` opens a configure menu that loops until Done (or Esc). "Change model" runs the same searchable model picker setup uses (the AI Gateway catalog, pre-selected on the model the runtime is serving); a model change is written into your agent's authored source, and the command reports success only after Eve confirms the new id (`/model <provider/model-id>` applies one directly, skipping the menu). The provider row opens the provider questions: which model provider to use (picking something other than AI Gateway shows wiring instructions for your own provider and stops there, leaving any existing setup untouched) and how to connect to AI Gateway — paste your own `AI_GATEWAY_API_KEY`, saved straight to `.env.local`, or connect via a project, which asks for a Vercel team and then opens that team's existing-project list (picking again re-links) before pulling the project's environment so an AI Gateway credential lands in `.env.local`; the dev server reloads env files automatically, no restart needed. The row demands attention (a bold yellow "Configure provider" with "Required to enable the agent") until a link or gateway credential is detected, then names the connection (e.g. "AI Gateway (Linked to my-project in my-team)") after, and each action's latest outcome stays visible beneath the menu (e.g. "✓ Model changed to openai/gpt-5.5"). `/channels` shows the agent's channel list — already-registered channels render as checked, focusable rows with an "Already installed" hint — and adds the one you pick, including the Slack Connect provisioning, then installs the dependencies the scaffold added so the dev server can load the new channels right away; after each addition the list repaints with the channel checked, until Done (or Esc) leaves the flow. `/deploy` ships the agent to Vercel production, linking first when the directory is unlinked. Each command echoes as an invocation line, asks through a bordered panel that takes the input area's place — one question at a time, clearly separate from the chat transcript — and finishes with a one-line `⎿` result; loading states stay on the ephemeral status line instead of piling into the transcript. These commands are not available when connected to a remote server with `--url`, and when a turn fails because AI Gateway authentication is missing or stale, the error points you at `/model` directly. The TUI also checks at startup: a missing model-provider setup surfaces as an attention line — `⚠ 1 setup issue: model provider not linked · /model` — so the fix is visible before the first message fails, and a logged-out Vercel session surfaces the same way as `⚠ 1 setup issue: not logged in · /login` (or `Vercel CLI not found · /vc` when the CLI itself is absent). Each command's outcome hangs under it with the `⎿` connector.
24
+
25
+ In the AI Gateway connection picker, "Connect via a project" is disabled when the Vercel CLI is missing, the CLI session is logged out, or Vercel cannot be reached. The disabled reason points to `/vc`, `/login`, or the network problem, while "Use my own key" remains available.
24
26
 
25
27
  The prompt input behaves like a shell line editor: `↑`/`↓` cycle through the messages you've sent this session, `←`/`→`, Home/End, and `Ctrl+A`/`Ctrl+E` move the caret, and `Ctrl+U`/`Ctrl+K`/`Ctrl+W` kill the line, the rest of the line, or the previous word. If a turn fails terminally — the server session dies or the connection drops — the TUI starts a fresh session and notes it inline so you can keep going (server-side context resets with the old session). Errors render compactly, with docs links highlighted, and a code bug escaping your agent's own code shows its stack trace dim beneath the error headline. Captured server `stdout`/`stderr` renders as dim, indented log runs behind a `│` rule — consecutive lines from the same source share one label — while sandbox lifecycle lines use their own label. Dev-server rebuilds condense further, into one status row that updates in place: `tui/setup-panel.ts changed · rebuilding…`, then `· rebuilt`. Only the latest rebuild shows, and paths shrink to their last two components. By default, `eve dev` shows `stderr` and keeps stdout and sandbox lines buffered but hidden. `/loglevel <all|stderr|sandbox|none>` switches what the transcript shows retroactively, and `--logs` sets the starting mode. Bare `/loglevel` reports the current mode. At the idle prompt, `Ctrl+L` cycles `none → all → stderr → sandbox → none` and briefly shows the selected mode in the status line; `Ctrl+R` redraws.
26
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -265,16 +265,15 @@
265
265
  },
266
266
  "dependencies": {
267
267
  "nitro": "3.0.260610-beta",
268
- "oxc-parser": "0.134.0",
269
268
  "semver": "7.8.4"
270
269
  },
271
270
  "devDependencies": {
272
- "@ai-sdk/anthropic": "4.0.0-canary.65",
273
- "@ai-sdk/google": "4.0.0-canary.80",
274
- "@ai-sdk/mcp": "2.0.0-canary.62",
275
- "@ai-sdk/openai": "4.0.0-canary.72",
276
- "@ai-sdk/otel": "1.0.0-canary.117",
277
- "@ai-sdk/provider": "4.0.0-canary.18",
271
+ "@ai-sdk/anthropic": "4.0.0-beta.67",
272
+ "@ai-sdk/google": "4.0.0-beta.82",
273
+ "@ai-sdk/mcp": "2.0.0-beta.66",
274
+ "@ai-sdk/openai": "4.0.0-beta.74",
275
+ "@ai-sdk/otel": "1.0.0-beta.124",
276
+ "@ai-sdk/provider": "4.0.0-beta.19",
278
277
  "@chat-adapter/slack": "4.29.0",
279
278
  "@chat-adapter/state-memory": "4.29.0",
280
279
  "@clack/core": "1.3.1",
@@ -284,13 +283,14 @@
284
283
  "@types/json-schema": "7.0.15",
285
284
  "@types/react": "19.2.15",
286
285
  "@types/react-test-renderer": "19.1.0",
286
+ "@vercel/detect-agent": "1.2.3",
287
287
  "@vercel/oidc": "3.5.0",
288
288
  "@vercel/sandbox": "2.2.1",
289
- "@workflow/core": "5.0.0-beta.15",
290
- "@workflow/errors": "5.0.0-beta.7",
291
- "@workflow/world": "5.0.0-beta.9",
292
- "@workflow/world-local": "5.0.0-beta.16",
293
- "ai": ">=7.0.0-beta.177",
289
+ "@workflow/core": "5.0.0-beta.17",
290
+ "@workflow/errors": "5.0.0-beta.8",
291
+ "@workflow/world": "5.0.0-beta.10",
292
+ "@workflow/world-local": "5.0.0-beta.18",
293
+ "ai": "7.0.0-beta.178",
294
294
  "autoevals": "0.0.132",
295
295
  "chat": "4.29.0",
296
296
  "chokidar": "5.0.0",
@@ -318,7 +318,7 @@
318
318
  "peerDependencies": {
319
319
  "@opentelemetry/api": "^1.0.0",
320
320
  "@sveltejs/kit": "^2.0.0",
321
- "ai": ">=7.0.0-beta.177",
321
+ "ai": "7.0.0-beta.178",
322
322
  "braintrust": "^3.0.0",
323
323
  "just-bash": "^3.0.0",
324
324
  "microsandbox": "^0.5.0",
@@ -371,10 +371,10 @@
371
371
  "clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
372
372
  "build": "pnpm run check:bin-runtime-dependencies && pnpm run build:js && node ./scripts/copy-docs.mjs && node ./scripts/stamp-version-tokens.mjs",
373
373
  "build:compiled": "node ./scripts/vendor-compiled.mjs",
374
- "build:js": "pnpm run build:compiled && pnpm run clean && pnpm run build:types && node ./scripts/copy-compiled-assets.mjs && node ./scripts/build-rolldown.mjs",
374
+ "build:js": "pnpm run build:compiled && pnpm run clean && pnpm run build:types && node ./scripts/copy-compiled-assets.mjs && node ./scripts/build-rolldown.mjs && node ./scripts/copy-runtime-assets.mjs",
375
375
  "build:types": "pnpm run build:compiled && tsgo -p tsconfig.build.json",
376
376
  "check:bin-runtime-dependencies": "node ./scripts/check-bin-runtime-dependencies.mjs",
377
- "dev": "pnpm run build:compiled && tsgo -p tsconfig.dev.json --watch --preserveWatchOutput",
377
+ "dev": "pnpm run build:compiled && node ./scripts/copy-runtime-assets.mjs && tsgo -p tsconfig.dev.json --watch --preserveWatchOutput",
378
378
  "test": "pnpm run test:unit && pnpm run test:integration",
379
379
  "test:integration": "pnpm run build:js && vitest run --config vitest.integration.config.ts",
380
380
  "test:scenario": "pnpm run build:compiled && vitest run --config vitest.scenario.config.ts",