eve 0.15.4 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/src/chunks/{use-eve-agent-8X2UMr8q.js → use-eve-agent-BEOUv37s.js} +70 -0
  3. package/dist/src/chunks/{use-eve-agent-9ZNiSFMb.js → use-eve-agent-C25KOe9i.js} +70 -0
  4. package/dist/src/cli/commands/init.js +1 -1
  5. package/dist/src/cli/dev/tui/agent-header.js +1 -1
  6. package/dist/src/cli/dev/tui/blocks.js +8 -8
  7. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  8. package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
  9. package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
  10. package/dist/src/cli/dev/tui/runner.d.ts +10 -3
  11. package/dist/src/cli/dev/tui/runner.js +1 -1
  12. package/dist/src/cli/dev/tui/setup-commands.d.ts +9 -1
  13. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  14. package/dist/src/cli/dev/tui/setup-flow.d.ts +8 -1
  15. package/dist/src/cli/dev/tui/setup-issues.d.ts +2 -14
  16. package/dist/src/cli/dev/tui/setup-issues.js +1 -1
  17. package/dist/src/cli/dev/tui/setup-panel.d.ts +14 -8
  18. package/dist/src/cli/dev/tui/setup-panel.js +3 -3
  19. package/dist/src/cli/dev/tui/terminal-renderer.js +6 -6
  20. package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
  21. package/dist/src/cli/dev/tui/tui.d.ts +2 -3
  22. package/dist/src/cli/dev/tui/tui.js +1 -1
  23. package/dist/src/cli/dev/ui-options.d.ts +21 -0
  24. package/dist/src/cli/dev/ui-options.js +1 -0
  25. package/dist/src/cli/run.d.ts +6 -42
  26. package/dist/src/cli/run.js +2 -2
  27. package/dist/src/client/authorization-message-parts.d.ts +4 -0
  28. package/dist/src/client/authorization-message-parts.js +1 -0
  29. package/dist/src/client/index.d.ts +2 -2
  30. package/dist/src/client/message-reducer-types.d.ts +40 -1
  31. package/dist/src/client/message-reducer.d.ts +3 -5
  32. package/dist/src/client/message-reducer.js +1 -1
  33. package/dist/src/compiled/.vendor-stamp.json +1 -1
  34. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +71 -1
  35. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  36. package/dist/src/harness/emission.d.ts +3 -3
  37. package/dist/src/harness/emission.js +1 -1
  38. package/dist/src/harness/stream-actions.d.ts +17 -0
  39. package/dist/src/harness/stream-actions.js +1 -0
  40. package/dist/src/internal/application/package.js +1 -1
  41. package/dist/src/internal/nitro/host/configure-nitro-routes.js +2 -2
  42. package/dist/src/internal/nitro/host/dev-authored-source-watcher.d.ts +1 -0
  43. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  44. package/dist/src/internal/nitro/host/start-development-server.d.ts +8 -0
  45. package/dist/src/internal/nitro/host/start-development-server.js +2 -2
  46. package/dist/src/internal/nitro/host.d.ts +1 -1
  47. package/dist/src/internal/nitro/host.js +1 -1
  48. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  49. package/dist/src/internal/nitro/routes/dev-runtime-artifacts.d.ts +0 -3
  50. package/dist/src/internal/nitro/routes/dev-runtime-artifacts.js +1 -1
  51. package/dist/src/internal/nitro/routes/info.js +1 -1
  52. package/dist/src/internal/vercel/project-link.d.ts +10 -0
  53. package/dist/src/internal/vercel/project-link.js +1 -0
  54. package/dist/src/protocol/message.d.ts +14 -10
  55. package/dist/src/public/channels/auth.d.ts +26 -13
  56. package/dist/src/public/channels/auth.js +1 -1
  57. package/dist/src/react/index.d.ts +1 -1
  58. package/dist/src/runtime/agent/mock-model-adapter.d.ts +3 -2
  59. package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
  60. package/dist/src/runtime/connections/principal.d.ts +2 -0
  61. package/dist/src/runtime/connections/principal.js +1 -1
  62. package/dist/src/runtime/connections/types.d.ts +4 -1
  63. package/dist/src/runtime/framework-channels/index.d.ts +1 -6
  64. package/dist/src/runtime/framework-channels/index.js +1 -1
  65. package/dist/src/runtime/governance/auth/oidc.js +1 -1
  66. package/dist/src/runtime/governance/auth/types.d.ts +14 -10
  67. package/dist/src/runtime/governance/auth/vercel-oidc-project.d.ts +12 -0
  68. package/dist/src/runtime/governance/auth/vercel-oidc-project.js +1 -0
  69. package/dist/src/services/dev-client/client-options.d.ts +5 -0
  70. package/dist/src/services/dev-client/client-options.js +1 -1
  71. package/dist/src/services/dev-client/request-headers.d.ts +8 -0
  72. package/dist/src/services/dev-client/request-headers.js +1 -1
  73. package/dist/src/services/dev-client/runtime-artifacts.d.ts +1 -0
  74. package/dist/src/services/dev-client/runtime-artifacts.js +1 -1
  75. package/dist/src/services/dev-client.d.ts +8 -0
  76. package/dist/src/services/dev-client.js +1 -1
  77. package/dist/src/setup/boxes/add-connections.d.ts +10 -8
  78. package/dist/src/setup/boxes/add-connections.js +1 -1
  79. package/dist/src/setup/boxes/resolve-provisioning.d.ts +1 -0
  80. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  81. package/dist/src/setup/cli/channel-setup-prompter.d.ts +9 -2
  82. package/dist/src/setup/cli/channel-setup-prompter.js +1 -1
  83. package/dist/src/setup/cli/index.d.ts +1 -1
  84. package/dist/src/setup/cli/prompt-ui.d.ts +2 -0
  85. package/dist/src/setup/cli/select-state.js +1 -1
  86. package/dist/src/setup/connection-connector.d.ts +26 -52
  87. package/dist/src/setup/connection-connector.js +2 -1
  88. package/dist/src/setup/flows/channels.js +1 -1
  89. package/dist/src/setup/flows/connections.d.ts +36 -0
  90. package/dist/src/setup/flows/connections.js +3 -0
  91. package/dist/src/setup/flows/link.d.ts +1 -0
  92. package/dist/src/setup/flows/link.js +1 -1
  93. package/dist/src/setup/project-resolution.d.ts +2 -8
  94. package/dist/src/setup/project-resolution.js +1 -1
  95. package/dist/src/setup/prompter.d.ts +2 -0
  96. package/dist/src/setup/scaffold/connections/catalog.d.ts +10 -5
  97. package/dist/src/setup/scaffold/connections/catalog.js +1 -1
  98. package/dist/src/setup/scaffold/create/project.js +11 -11
  99. package/dist/src/setup/scaffold/create/web-template.d.ts +2 -2
  100. package/dist/src/setup/scaffold/create/web-template.js +107 -3
  101. package/dist/src/setup/scaffold/index.d.ts +1 -1
  102. package/dist/src/setup/scaffold/index.js +1 -1
  103. package/dist/src/setup/scaffold/update/connections.d.ts +6 -0
  104. package/dist/src/setup/scaffold/update/connections.js +3 -3
  105. package/dist/src/svelte/index.d.ts +1 -1
  106. package/dist/src/svelte/index.js +1 -1
  107. package/dist/src/svelte/use-eve-agent.js +1 -1
  108. package/dist/src/vue/index.d.ts +1 -1
  109. package/dist/src/vue/index.js +1 -1
  110. package/dist/src/vue/use-eve-agent.js +1 -1
  111. package/docs/channels/eve.mdx +3 -3
  112. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  113. package/docs/guides/auth-and-route-protection.md +5 -5
  114. package/docs/guides/dev-tui.md +11 -4
  115. package/docs/guides/frontend/nextjs.mdx +2 -2
  116. package/docs/guides/frontend/nuxt.mdx +2 -2
  117. package/docs/guides/frontend/overview.mdx +34 -4
  118. package/docs/guides/frontend/sveltekit.mdx +2 -2
  119. package/docs/reference/cli.md +3 -1
  120. package/docs/tutorial/ship-it.mdx +1 -1
  121. package/docs/tutorial/team-playbooks.mdx +1 -1
  122. package/package.json +2 -2
  123. package/dist/src/internal/nitro/host/dev-rebuild-registry.d.ts +0 -5
  124. package/dist/src/internal/nitro/host/dev-rebuild-registry.js +0 -1
@@ -37,6 +37,14 @@ export type DevelopmentOidcTokenResolution = {
37
37
  * first and install the result in a `DevelopmentCredentialGate`.
38
38
  */
39
39
  export declare function resolveDevelopmentOidcToken(input: DevelopmentOidcTarget): Promise<DevelopmentOidcTokenResolution>;
40
+ /**
41
+ * Resolves the current linked project's Vercel OIDC token for a local TUI request.
42
+ *
43
+ * An unavailable token deliberately becomes no bearer, so ordinary local
44
+ * requests still reach `localDev()`; user-scoped Connect requests then report
45
+ * that they need a Vercel user rather than trusting a client-supplied failure.
46
+ */
47
+ export declare function resolveLinkedDevelopmentOidcToken(workspaceRoot: string): Promise<string>;
40
48
  /**
41
49
  * Vercel header used to bypass preview protection for framework-owned routes
42
50
  * during local CLI development. Paired with a Protection Bypass for
@@ -1 +1 @@
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";import{toErrorMessage}from"#shared/errors.js";import{getVercelOidcToken}from"#compiled/@vercel/oidc/index.js";const VercelOidcClaimsSchema=object({owner_id:string().min(1),project_id:string().min(1)});async function resolveDevelopmentOidcToken(e){try{let t={team:e.ownerId,project:e.projectId};return e.forceRefresh===!0&&(t.expirationBufferMs=2**53-1),validateDevelopmentOidcToken((await getVercelOidcToken(t)).trim(),e)}catch(e){return{kind:`resolution-failed`,message:toErrorMessage(e)}}}function validateDevelopmentOidcToken(e,t){let n=e.split(`.`)[1];if(!n)return{kind:`malformed-token`,reason:`missing-payload`};let r;try{r=JSON.parse(Buffer.from(n,`base64url`).toString(`utf8`))}catch{return{kind:`malformed-token`,reason:`invalid-json-payload`}}let i=VercelOidcClaimsSchema.safeParse(r);if(!i.success)return{kind:`invalid-claims`,invalidClaims:i.error.issues.map(e=>{let t=e.path[0];return t===`owner_id`||t===`project_id`?t:`claims`})};let a=[];return i.data.owner_id!==t.ownerId&&a.push(`owner_id`),i.data.project_id!==t.projectId&&a.push(`project_id`),a.length>0?{kind:`target-mismatch`,mismatchedClaims:a}:{kind:`resolved`,token:e}}const VERCEL_PROTECTION_BYPASS_HEADER=`x-vercel-protection-bypass`;export{VERCEL_PROTECTION_BYPASS_HEADER,resolveDevelopmentOidcToken};
1
+ import{literal,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";import{toErrorMessage}from"#shared/errors.js";import{getVercelOidcToken}from"#compiled/@vercel/oidc/index.js";import{readVercelProjectLink}from"#internal/vercel/project-link.js";const VercelOidcClaimsSchema=object({owner_id:string().min(1),project_id:string().min(1)}),LocalDevelopmentUserOidcClaimsSchema=VercelOidcClaimsSchema.extend({environment:literal(`development`),user_id:string().min(1)});async function resolveDevelopmentOidcToken(e){try{let t={team:e.ownerId,project:e.projectId};return e.forceRefresh===!0&&(t.expirationBufferMs=2**53-1),validateDevelopmentOidcToken((await getVercelOidcToken(t)).trim(),e)}catch(e){return{kind:`resolution-failed`,message:toErrorMessage(e)}}}async function resolveLinkedDevelopmentOidcToken(e){let t=await readVercelProjectLink(e);if(t===void 0)return``;let n={ownerId:t.orgId,projectId:t.projectId},r=await resolveDevelopmentOidcToken(n);if(r.kind===`resolved`&&isLocalDevelopmentUserToken(r.token))return r.token;if(r.kind!==`resolved`&&r.kind!==`target-mismatch`)return``;let i=await resolveDevelopmentOidcToken({...n,forceRefresh:!0});return i.kind===`resolved`&&isLocalDevelopmentUserToken(i.token)?i.token:``}function isLocalDevelopmentUserToken(e){let t=decodeOidcPayload(e);return t!==void 0&&LocalDevelopmentUserOidcClaimsSchema.safeParse(t).success}function validateDevelopmentOidcToken(e,t){let n=decodeOidcPayload(e);if(n===void 0)return e.split(`.`)[1]?{kind:`malformed-token`,reason:`invalid-json-payload`}:{kind:`malformed-token`,reason:`missing-payload`};let r=VercelOidcClaimsSchema.safeParse(n);if(!r.success)return{kind:`invalid-claims`,invalidClaims:r.error.issues.map(e=>{let t=e.path[0];return t===`owner_id`||t===`project_id`?t:`claims`})};let i=[];return r.data.owner_id!==t.ownerId&&i.push(`owner_id`),r.data.project_id!==t.projectId&&i.push(`project_id`),i.length>0?{kind:`target-mismatch`,mismatchedClaims:i}:{kind:`resolved`,token:e}}function decodeOidcPayload(e){let t=e.split(`.`)[1];if(t)try{return JSON.parse(Buffer.from(t,`base64url`).toString(`utf8`))}catch{return}}const VERCEL_PROTECTION_BYPASS_HEADER=`x-vercel-protection-bypass`;export{VERCEL_PROTECTION_BYPASS_HEADER,resolveDevelopmentOidcToken,resolveLinkedDevelopmentOidcToken};
@@ -12,5 +12,6 @@ export declare function readDevelopmentRuntimeArtifactsRevision(input: {
12
12
  readonly serverUrl: string;
13
13
  }): Promise<string | undefined>;
14
14
  export declare function rebuildDevelopmentRuntimeArtifacts(input: {
15
+ readonly force?: boolean;
15
16
  readonly serverUrl: string;
16
17
  }): Promise<string | undefined>;
@@ -1 +1 @@
1
- import{EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH}from"#protocol/routes.js";async function readDevelopmentRuntimeArtifactsRevision(e){try{let n=new URL(EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH,e.serverUrl);return await parseDevelopmentRuntimeArtifactsRevision(await fetch(n))}catch{return}}async function rebuildDevelopmentRuntimeArtifacts(t){try{let n=new URL(EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH,t.serverUrl);return await parseDevelopmentRuntimeArtifactsRevision(await fetch(n,{method:`POST`}))}catch{return}}async function parseDevelopmentRuntimeArtifactsRevision(e){if(!e.ok)return;let t=await e.json();return typeof t.revision==`string`&&t.revision.length>0?t.revision:void 0}export{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts};
1
+ import{EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH}from"#protocol/routes.js";async function readDevelopmentRuntimeArtifactsRevision(e){try{let n=new URL(EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH,e.serverUrl);return await parseDevelopmentRuntimeArtifactsRevision(await fetch(n))}catch{return}}async function rebuildDevelopmentRuntimeArtifacts(t){try{let n=new URL(EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH,t.serverUrl);return t.force===!0&&n.searchParams.set(`force`,`1`),await parseDevelopmentRuntimeArtifactsRevision(await fetch(n,{method:`POST`}))}catch{return}}async function parseDevelopmentRuntimeArtifactsRevision(e){if(!e.ok)return;let t=await e.json();return typeof t.revision==`string`&&t.revision.length>0?t.revision:void 0}export{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts};
@@ -21,6 +21,14 @@ export interface DevelopmentRuntimeArtifactSessionRefresher {
21
21
  readonly onRuntimeArtifactsChanged?: (change: DevelopmentRuntimeArtifactChange) => void | Promise<void>;
22
22
  readonly session: ClientSession;
23
23
  }): Promise<ClientSession>;
24
+ /**
25
+ * Forces one rebuild after a local setup action wrote authored source.
26
+ */
27
+ refreshAfterSourceChange(input: {
28
+ readonly createSession: () => ClientSession;
29
+ readonly onRuntimeArtifactsChanged?: (change: DevelopmentRuntimeArtifactChange) => void | Promise<void>;
30
+ readonly session: ClientSession;
31
+ }): Promise<ClientSession>;
24
32
  /**
25
33
  * Checks for a runtime-artifact revision change while the UI is idle.
26
34
  */
@@ -1 +1 @@
1
- import{isLocalDevelopmentServerUrl}from"#services/dev-client/local-host.js";import{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts}from"#services/dev-client/runtime-artifacts.js";var LocalDevelopmentRuntimeArtifactSessionRefresher=class{#e;#t;#n;constructor(t){this.#e=isLocalDevelopmentServerUrl(t.serverUrl),this.#t=t.serverUrl}clear(){this.#n=void 0}async refresh(e){return shouldRefreshRuntimeArtifactsForTurn(e)?await this.#r({...e,rebuild:!0}):e.session}async refreshIdle(e){return await this.#r({...e,rebuild:!1})}async#r(e){if(!this.#e)return e.session;let n=(e.rebuild?await rebuildDevelopmentRuntimeArtifacts({serverUrl:this.#t}):void 0)??await readDevelopmentRuntimeArtifactsRevision({serverUrl:this.#t});if(n===void 0)return e.session;let r=e.session,i=this.#n;return i!==void 0&&i!==n&&(r.state.continuationToken!==void 0&&(r=e.createSession()),await e.onRuntimeArtifactsChanged?.({previousRevision:i,revision:n})),this.#n=n,r}};function shouldRefreshRuntimeArtifactsForTurn(e){return e.message!==void 0&&(e.inputResponses?.length??0)===0}function createDevelopmentRuntimeArtifactSessionRefresher(e){return new LocalDevelopmentRuntimeArtifactSessionRefresher(e)}export{createDevelopmentRuntimeArtifactSessionRefresher};
1
+ import{isLocalDevelopmentServerUrl}from"#services/dev-client/local-host.js";import{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts}from"#services/dev-client/runtime-artifacts.js";var LocalDevelopmentRuntimeArtifactSessionRefresher=class{#e;#t;#n;constructor(t){this.#e=isLocalDevelopmentServerUrl(t.serverUrl),this.#t=t.serverUrl}clear(){this.#n=void 0}async refresh(e){return shouldRefreshRuntimeArtifactsForTurn(e)?await this.#r({...e,rebuild:!0}):e.session}async refreshIdle(e){return await this.#r({...e,rebuild:!1})}async refreshAfterSourceChange(e){return await this.#r({...e,force:!0,rebuild:!0,resetActiveSession:!0})}async#r(e){if(!this.#e)return e.session;let n=(e.rebuild?await rebuildDevelopmentRuntimeArtifacts({force:e.force,serverUrl:this.#t}):void 0)??await readDevelopmentRuntimeArtifactsRevision({serverUrl:this.#t});if(n===void 0)return e.session;let r=e.session,i=this.#n,a=i!==void 0&&i!==n;return(a||e.resetActiveSession===!0)&&r.state.continuationToken!==void 0&&(r=e.createSession()),a&&await e.onRuntimeArtifactsChanged?.({previousRevision:i,revision:n}),this.#n=n,r}};function shouldRefreshRuntimeArtifactsForTurn(e){return e.message!==void 0&&(e.inputResponses?.length??0)===0}function createDevelopmentRuntimeArtifactSessionRefresher(e){return new LocalDevelopmentRuntimeArtifactSessionRefresher(e)}export{createDevelopmentRuntimeArtifactSessionRefresher};
@@ -1,24 +1,26 @@
1
- import { ensureConnection } from "#setup/scaffold/index.js";
2
- import { setupConnectionConnector } from "../connection-connector.js";
3
- import { type ProjectResolution } from "../project-resolution.js";
1
+ import { ensureConnection, listAuthoredConnections } from "#setup/scaffold/index.js";
2
+ import { cleanupCreatedConnectionConnector, setupConnectionConnector } from "../connection-connector.js";
3
+ import { readProjectLink, type ProjectResolution } from "../project-resolution.js";
4
4
  import type { Prompter } from "../prompter.js";
5
5
  import { type SetupState } from "../state.js";
6
6
  import type { SetupBox } from "../step.js";
7
7
  /** Injected for tests; defaults to the real scaffold and Connect effects. */
8
8
  export interface AddConnectionsDeps {
9
9
  ensureConnection: typeof ensureConnection;
10
+ listAuthoredConnections: typeof listAuthoredConnections;
11
+ readProjectLink: typeof readProjectLink;
10
12
  setupConnectionConnector: typeof setupConnectionConnector;
13
+ cleanupCreatedConnectionConnector: typeof cleanupCreatedConnectionConnector;
11
14
  }
12
15
  export interface AddConnectionsOptions {
13
- /** Carries the follow-up log lines and `perform`'s progress output. The box never prompts. */
16
+ /** Carries connector selection prompts and provisioning output. */
14
17
  prompter: Prompter;
18
+ signal?: AbortSignal;
15
19
  deps?: AddConnectionsDeps;
16
20
  }
17
21
  /**
18
22
  * THE CONNECTIONS BOX: executes the {@link ConnectionPlan}s the
19
- * select-connections box recorded during the interview. Prompts for nothing
20
- * every decision (slug, protocol, entry, provisioning mode) was resolved at
21
- * selection time — and only runs effects: the file scaffold, the follow-up log
22
- * lines, and the Connect connector provisioning against the linked project.
23
+ * select-connections box recorded during the interview. It scaffolds each file
24
+ * and resolves the concrete Connect connector against the linked project.
23
25
  */
24
26
  export declare function addConnections(options: AddConnectionsOptions): SetupBox<SetupState, null, ProjectResolution>;
@@ -1 +1 @@
1
- import{isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{hasVercelProject,requireProjectPath}from"../state.js";import{setupConnectionConnector}from"../connection-connector.js";import{projectIdFromResolution}from"../vercel-project.js";import{CONNECT_REQUIRES_VERCEL}from"./select-connections.js";import{ensureConnection}from"#setup/scaffold/index.js";function logFollowUp(e,t){if(t.action===`skipped`){e.warning(`Skipped ${t.slug} (already exists; pass --force to overwrite).`);return}e.success(`Added agent/connections/${t.slug}.ts`),t.envKeysAdded.length>0?e.info(`Set ${t.envKeysAdded.join(`, `)} in .env.local`):t.envKeysRequired.length>0&&e.info(`Set ${t.envKeysRequired.join(`, `)} in your environment`)}function addConnections(a){let o=a.deps??{ensureConnection,setupConnectionConnector};return{id:`add-connections`,shouldRun(e){return e.connectionSelection.length>0},async gather(){return null},async perform({state:t}){let r=a.prompter.log,i=requireProjectPath(t),s=!hasVercelProject(t),c=t.project;for(let n of t.connectionSelection){let t=await o.ensureConnection({projectRoot:i,slug:n.slug,protocol:n.protocol,entry:n.entry});if(logFollowUp(r,t),t.action!==`skipped`)switch(n.provision.kind){case`connect`:await o.setupConnectionConnector({log:r,projectRoot:i,slug:t.slug,service:n.provision.service,connectionFilePath:t.filePath,linkProject:async()=>{if(s)throw Error(CONNECT_REQUIRES_VERCEL);if(!isProjectResolved(c))throw Error(`Expected a linked Vercel project for Connect, but none was resolved.`);return projectIdFromResolution(c)}});break;case`command-hint`:r.info(`Run \`vercel connect create ${n.provision.service} --name ${t.slug}\`, then set the connector UID in agent/connections/${t.slug}.ts.`);break;case`connect-manual`:r.warning(`Could not determine a Connect service for ${t.slug}. Create the connector manually and set its UID in agent/connections/${t.slug}.ts.`);break;case`none`:break}}return c},apply(e,n){return{...e,project:mergeProjectResolution(e.project,n)}}}}export{addConnections};
1
+ import{isProjectResolved,mergeProjectResolution,readProjectLink}from"../project-resolution.js";import{hasVercelProject,requireProjectPath}from"../state.js";import{cleanupCreatedConnectionConnector,setupConnectionConnector}from"../connection-connector.js";import{canonicalConnectorNameForEntry}from"../scaffold/connections/catalog.js";import{projectIdFromResolution}from"../vercel-project.js";import{CONNECT_REQUIRES_VERCEL}from"./select-connections.js";import{ensureConnection,listAuthoredConnections}from"#setup/scaffold/index.js";function logFollowUp(e,t){if(t.action===`skipped`){e.warning(`Skipped ${t.slug} (already exists; pass --force to overwrite).`);return}e.success(`Added agent/connections/${t.slug}.ts`),t.envKeysAdded.length>0?e.info(`Set ${t.envKeysAdded.join(`, `)} in .env.local`):t.envKeysRequired.length>0&&e.info(`Set ${t.envKeysRequired.join(`, `)} in your environment`)}function withConnectorUid(e,t){if(e.auth?.kind!==`connect`)throw Error(`Connection ${e.slug} is not configured for Vercel Connect.`);return{...e,auth:{...e.auth,connector:t}}}function addConnections(e){let s=e.deps??{ensureConnection,listAuthoredConnections,readProjectLink,setupConnectionConnector,cleanupCreatedConnectionConnector};return{id:`add-connections`,shouldRun(e){return e.connectionSelection.length>0},async gather(){return null},async perform({state:t}){let n=e.prompter.log,a=requireProjectPath(t),o=!hasVercelProject(t),c=t.project,l=new Set(await s.listAuthoredConnections(a));for(let r of t.connectionSelection){if(l.has(r.slug)){logFollowUp(n,await s.ensureConnection({projectRoot:a,slug:r.slug,protocol:r.protocol,entry:r.entry}));continue}let t=r.entry,i;switch(r.provision.kind){case`connect`:{let l=canonicalConnectorNameForEntry(r.entry);if(l===void 0)throw Error(`Connection ${r.slug} has no canonical connector name.`);let u=await s.setupConnectionConnector({log:n,prompter:e.prompter,projectRoot:a,slug:r.slug,service:r.provision.service,canonicalConnectorName:l,project:await resolveConnectionProject({noVercel:o,project:c,projectRoot:a,readProjectLink:s.readProjectLink}),signal:e.signal});t=withConnectorUid(t,u.connectorUid),u.kind===`created`&&(i=u.connectorId);break}case`command-hint`:n.info(`Run \`vercel connect create ${r.provision.service} --name ${r.slug}\`, then set the connector UID in agent/connections/${r.slug}.ts.`);break;case`connect-manual`:n.warning(`Could not determine a Connect service for ${r.slug}. Create the connector manually and set its UID in agent/connections/${r.slug}.ts.`);break;case`none`:break}let u;try{u=await s.ensureConnection({projectRoot:a,slug:r.slug,protocol:r.protocol,entry:t})}catch(e){if(i!==void 0)try{await s.cleanupCreatedConnectionConnector({log:n,projectRoot:a,connectorId:i})}catch(t){throw AggregateError([e,t],`${e instanceof Error?e.message:String(e)} ${t instanceof Error?t.message:String(t)}`)}throw e}u.action===`skipped`&&i!==void 0&&await s.cleanupCreatedConnectionConnector({log:n,projectRoot:a,connectorId:i}),logFollowUp(n,u),u.action!==`skipped`&&l.add(u.slug)}return c},apply(e,n){return{...e,project:mergeProjectResolution(e.project,n)}}}}async function resolveConnectionProject(t){if(t.noVercel)throw Error(CONNECT_REQUIRES_VERCEL);if(!isProjectResolved(t.project))throw Error(`Expected a linked Vercel project for Connect, but none was resolved.`);let n=await t.readProjectLink(t.projectRoot);if(n===void 0||n.projectId!==projectIdFromResolution(t.project))throw Error("A linked Vercel project is required. Run `eve link`, then retry /connect.");return n}export{addConnections};
@@ -46,6 +46,7 @@ export interface ResolveProvisioningOptions {
46
46
  * team selection to the existing-project picker.
47
47
  */
48
48
  projectSelection?: "create-or-link" | "existing-only";
49
+ teamSelectMessage?: (currentTeam: string) => string;
49
50
  deps?: ResolveProvisioningDeps;
50
51
  }
51
52
  /** Both provisioning plans plus the model wiring they imply. Input and payload. */
@@ -1 +1 @@
1
- import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{withSpinner}from"../with-spinner.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveProjectByNameOrId,resolveTeam,validateTeam}from"../vercel-project.js";import{pathExists}from"../path-exists.js";import{join,resolve}from"node:path";import{byokProviderEnvVar}from"#setup/scaffold/index.js";import{whimsyFor}from"#setup/cli/index.js";function vercelDemands(e){return{slack:e.channelSelection.includes(`slack`),connectSlugs:e.connectionSelection.filter(e=>e.entry.auth?.kind===`connect`).map(e=>e.slug)}}function connectClause(e){let t=e.length===1?`authenticates`:`authenticate`;return`${e.join(`, `)} ${t} through Vercel Connect`}function vercelRequirements(e){let t=[];return e.slack&&t.push(`Slack needs a public URL`),e.connectSlugs.length>0&&t.push(connectClause(e.connectSlugs)),t}function resolveProvisioning(l){let u=l.deps??{requireAuth,isVercelAuthenticated,detectProjectResolution,pathExists,validateTeam,resolveTeam,pickTeam,pickProject,pickNewProjectName,assertNewProjectNameAvailable,resolveProjectByNameOrId},parent=()=>resolve(l.targetDirectory??process.cwd());async function detectAdoptableLink(e,t){if(e.projectPath.kind!==`resolved`)return;let n=e.projectPath.path;if(await u.pathExists(join(n,`.vercel`,`project.json`)))return withSpinner(l.prompter,whimsyFor(`project-detect`),async()=>{let e=await u.detectProjectResolution(n,{signal:t});if(!(!isProjectResolved(e)||!await u.isVercelAuthenticated(n,{signal:t})))return e})}async function plansFromFlags(e,t,n,r){if(t.skipVercel)return n.apiKey===void 0?{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:n.apiKey},modelWiring:`gateway`};if(t.team===void 0||t.team.length===0)throw Error(`Headless Vercel provisioning requires --team <slug> or --scope <slug> so the current CLI scope is not applied silently.`);await u.requireAuth(parent(),void 0,{signal:r}),await u.validateTeam(l.prompter,parent(),t.team,{signal:r});let i=await u.resolveTeam(parent(),t.team,{signal:r}),a=n.apiKey===void 0?{kind:`inherit`}:{kind:`byok`,apiGatewayKey:n.apiKey},o;if(t.project===void 0)o={kind:`new`,project:e,team:i};else{let e=await u.resolveProjectByNameOrId(parent(),i,t.project,{signal:r});if(e===null)throw Error(`Vercel project "${t.project}" was not found in ${i}.`);o={kind:`existing`,project:e,team:i}}return o.kind===`new`&&await u.assertNewProjectNameAvailable(parent(),i,o.project,{signal:r}),{vercelProject:o,aiGateway:a,modelWiring:`gateway`}}async function promptPlans(n,r){let i=l.prompter,a=n.agentName,o=l.adoptExistingLink===!1?void 0:await detectAdoptableLink(n,r);if(o!==void 0)return i.log.info(`This directory is already linked to a Vercel project, so your agent will run there — the AI Gateway authenticates through your Vercel login.`),{vercelProject:{kind:`none`},aiGateway:{kind:`inherit`},modelWiring:`gateway`,project:o};let s=vercelRequirements(vercelDemands(n)),c=!0;if(s.length>0?i.log.info(`${s.join(` and `)}, so your agent will run on Vercel.`):c=await l.asker.ask(select({key:`deploy`,message:`Where should your agent run?`,options:[{id:`vercel`,value:!0,label:`On Vercel`,hint:`AI Gateway, Durable Workflow, Sandbox, and more`},{id:`local`,value:!1,label:`Locally for now`,hint:`run with eve dev, deploy any time later`}],recommended:!0,required:!0})),c){await u.requireAuth(parent(),i,{signal:r});let t=await u.pickTeam(i,parent(),void 0,{signal:r}),n=[{value:`new`,label:`Create a new project`,hint:`Name: ${a}`},{value:`link`,label:`Link an existing project`}],o=l.projectSelection!==`existing-only`&&l.prompter.selectEditable?await l.prompter.selectEditable({message:`Vercel project`,options:n,initialValue:`new`,editable:{value:`new`,defaultValue:a,formatHint:e=>`Name: ${e}`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}}):void 0;if((l.projectSelection===`existing-only`?`link`:o?.value??await l.asker.ask(select({key:`vercel-project`,message:`Vercel project`,options:[{id:`new`,value:`new`,label:`Create a new project`,hint:`Name: ${a}`},{id:`link`,value:`link`,label:`Link an existing project`}],recommended:`new`,required:!0})))===`new`){let e=o?.kind===`edited`?o.text:a;return{vercelProject:{kind:`new`,project:await u.pickNewProjectName(i,parent(),t,e,{signal:r}),team:t},aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return{vercelProject:await u.pickProject(i,parent(),t,{allowCreateWhenEmpty:l.projectSelection!==`existing-only`,signal:r}),aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return await l.asker.ask(select({key:`credential`,message:`How should your agent reach the model?`,options:[{id:`api-key`,value:`api-key`,label:`Use my own AI Gateway API key`,hint:`AI_GATEWAY_API_KEY`},{id:`local`,value:`local`,label:`Use my own provider API key`,hint:byokProviderEnvVar(n.modelId)}],recommended:`api-key`,required:!0}))===`api-key`?{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:await l.asker.ask(text({key:`gateway-api-key`,message:`Enter your AI_GATEWAY_API_KEY`,sensitive:!0,validate:e=>e.trim().length===0?`API key cannot be empty.`:null,required:!0}))},modelWiring:`gateway`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}}return{id:`resolve-provisioning`,async gather({state:e,signal:t}){if(l.mode.headless){let n=await plansFromFlags(e.agentName,l.mode.project,l.mode.aiGateway,t);if(n.vercelProject.kind===`none`){let t=vercelDemands(e);if(t.slack)throw Error(`Slack requires a Vercel project. Remove --skip-vercel to add Slack.`);if(t.connectSlugs.length>0)throw Error(`${connectClause(t.connectSlugs)}, which needs a Vercel project. Remove --skip-vercel to add ${t.connectSlugs.length===1?`it`:`them`}.`)}return n}return promptPlans(e,t)},async perform({input:e}){return e},apply(e,t){return{...e,vercelProject:t.vercelProject,aiGateway:t.aiGateway,modelWiring:t.modelWiring,project:t.project===void 0?e.project:mergeProjectResolution(e.project,t.project)}}}}export{resolveProvisioning};
1
+ import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{withSpinner}from"../with-spinner.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveProjectByNameOrId,resolveTeam,validateTeam}from"../vercel-project.js";import{pathExists}from"../path-exists.js";import{join,resolve}from"node:path";import{byokProviderEnvVar}from"#setup/scaffold/index.js";import{whimsyFor}from"#setup/cli/index.js";function vercelDemands(e){return{slack:e.channelSelection.includes(`slack`),connectSlugs:e.connectionSelection.filter(e=>e.entry.auth?.kind===`connect`).map(e=>e.slug)}}function connectClause(e){let t=e.length===1?`authenticates`:`authenticate`;return`${e.join(`, `)} ${t} through Vercel Connect`}function vercelRequirements(e){let t=[];return e.slack&&t.push(`Slack needs a public URL`),e.connectSlugs.length>0&&t.push(connectClause(e.connectSlugs)),t}function resolveProvisioning(l){let u=l.deps??{requireAuth,isVercelAuthenticated,detectProjectResolution,pathExists,validateTeam,resolveTeam,pickTeam,pickProject,pickNewProjectName,assertNewProjectNameAvailable,resolveProjectByNameOrId},parent=()=>resolve(l.targetDirectory??process.cwd());async function detectAdoptableLink(e,t){if(e.projectPath.kind!==`resolved`)return;let n=e.projectPath.path;if(await u.pathExists(join(n,`.vercel`,`project.json`)))return withSpinner(l.prompter,whimsyFor(`project-detect`),async()=>{let e=await u.detectProjectResolution(n,{signal:t});if(!(!isProjectResolved(e)||!await u.isVercelAuthenticated(n,{signal:t})))return e})}async function plansFromFlags(e,t,n,r){if(t.skipVercel)return n.apiKey===void 0?{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:n.apiKey},modelWiring:`gateway`};if(t.team===void 0||t.team.length===0)throw Error(`Headless Vercel provisioning requires --team <slug> or --scope <slug> so the current CLI scope is not applied silently.`);await u.requireAuth(parent(),void 0,{signal:r}),await u.validateTeam(l.prompter,parent(),t.team,{signal:r});let i=await u.resolveTeam(parent(),t.team,{signal:r}),a=n.apiKey===void 0?{kind:`inherit`}:{kind:`byok`,apiGatewayKey:n.apiKey},o;if(t.project===void 0)o={kind:`new`,project:e,team:i};else{let e=await u.resolveProjectByNameOrId(parent(),i,t.project,{signal:r});if(e===null)throw Error(`Vercel project "${t.project}" was not found in ${i}.`);o={kind:`existing`,project:e,team:i}}return o.kind===`new`&&await u.assertNewProjectNameAvailable(parent(),i,o.project,{signal:r}),{vercelProject:o,aiGateway:a,modelWiring:`gateway`}}async function promptPlans(n,r){let i=l.prompter,a=n.agentName,o=l.adoptExistingLink===!1?void 0:await detectAdoptableLink(n,r);if(o!==void 0)return i.log.info(`This directory is already linked to a Vercel project, so your agent will run there — the AI Gateway authenticates through your Vercel login.`),{vercelProject:{kind:`none`},aiGateway:{kind:`inherit`},modelWiring:`gateway`,project:o};let s=vercelRequirements(vercelDemands(n)),c=!0;if(s.length>0?i.log.info(`${s.join(` and `)}, so your agent will run on Vercel.`):c=await l.asker.ask(select({key:`deploy`,message:`Where should your agent run?`,options:[{id:`vercel`,value:!0,label:`On Vercel`,hint:`AI Gateway, Durable Workflow, Sandbox, and more`},{id:`local`,value:!1,label:`Locally for now`,hint:`run with eve dev, deploy any time later`}],recommended:!0,required:!0})),c){await u.requireAuth(parent(),i,{signal:r});let t={signal:r,selectMessage:l.teamSelectMessage},n=await u.pickTeam(i,parent(),void 0,t),o=[{value:`new`,label:`Create a new project`,hint:`Name: ${a}`},{value:`link`,label:`Link an existing project`}],s=l.projectSelection!==`existing-only`&&l.prompter.selectEditable?await l.prompter.selectEditable({message:`Vercel project`,options:o,initialValue:`new`,editable:{value:`new`,defaultValue:a,formatHint:e=>`Name: ${e}`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}}):void 0;if((l.projectSelection===`existing-only`?`link`:s?.value??await l.asker.ask(select({key:`vercel-project`,message:`Vercel project`,options:[{id:`new`,value:`new`,label:`Create a new project`,hint:`Name: ${a}`},{id:`link`,value:`link`,label:`Link an existing project`}],recommended:`new`,required:!0})))===`new`){let e=s?.kind===`edited`?s.text:a;return{vercelProject:{kind:`new`,project:await u.pickNewProjectName(i,parent(),n,e,{signal:r}),team:n},aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return{vercelProject:await u.pickProject(i,parent(),n,{allowCreateWhenEmpty:l.projectSelection!==`existing-only`,signal:r}),aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return await l.asker.ask(select({key:`credential`,message:`How should your agent reach the model?`,options:[{id:`api-key`,value:`api-key`,label:`Use my own AI Gateway API key`,hint:`AI_GATEWAY_API_KEY`},{id:`local`,value:`local`,label:`Use my own provider API key`,hint:byokProviderEnvVar(n.modelId)}],recommended:`api-key`,required:!0}))===`api-key`?{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:await l.asker.ask(text({key:`gateway-api-key`,message:`Enter your AI_GATEWAY_API_KEY`,sensitive:!0,validate:e=>e.trim().length===0?`API key cannot be empty.`:null,required:!0}))},modelWiring:`gateway`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}}return{id:`resolve-provisioning`,async gather({state:e,signal:t}){if(l.mode.headless){let n=await plansFromFlags(e.agentName,l.mode.project,l.mode.aiGateway,t);if(n.vercelProject.kind===`none`){let t=vercelDemands(e);if(t.slack)throw Error(`Slack requires a Vercel project. Remove --skip-vercel to add Slack.`);if(t.connectSlugs.length>0)throw Error(`${connectClause(t.connectSlugs)}, which needs a Vercel project. Remove --skip-vercel to add ${t.connectSlugs.length===1?`it`:`them`}.`)}return n}return promptPlans(e,t)},async perform({input:e}){return e},apply(e,t){return{...e,vercelProject:t.vercelProject,aiGateway:t.aiGateway,modelWiring:t.modelWiring,project:t.project===void 0?e.project:mergeProjectResolution(e.project,t.project)}}}}export{resolveProvisioning};
@@ -22,6 +22,13 @@ export interface ChannelSetupChoice {
22
22
  }
23
23
  /** Optional interaction capability for a long-running setup operation. */
24
24
  export type ChannelSetupAwaitChoice = (options: ChannelSetupChoiceOptions) => ChannelSetupChoice;
25
+ /** Why a live setup indicator is waiting. */
26
+ export type SetupSpinnerIntent = {
27
+ kind: "progress";
28
+ } | {
29
+ kind: "external-action";
30
+ emphasis: string;
31
+ };
25
32
  /** Status and subprocess output operations used by shared setup flows. */
26
33
  export interface ChannelSetupLog {
27
34
  message(text: string): void;
@@ -38,7 +45,7 @@ export interface ChannelSetupLog {
38
45
  * (which have no live status surface) can omit it and callers fall back to
39
46
  * a persisted message.
40
47
  */
41
- spinner?(message: string): {
48
+ spinner?(message: string, intent?: SetupSpinnerIntent): {
42
49
  stop(): void;
43
50
  };
44
51
  }
@@ -48,4 +55,4 @@ export interface ChannelSetupLog {
48
55
  * phase message instead, keeping their progress trail; the spinner stops
49
56
  * whether the work resolves or throws.
50
57
  */
51
- export declare function withPhase<T>(log: ChannelSetupLog, message: string, task: () => Promise<T>): Promise<T>;
58
+ export declare function withPhase<T>(log: ChannelSetupLog, message: string, task: () => Promise<T>, intent?: SetupSpinnerIntent): Promise<T>;
@@ -1 +1 @@
1
- async function withPhase(e,t,n){let r=e.spinner?.(t);r||e.message(t);try{return await n()}finally{r?.stop()}}export{withPhase};
1
+ async function withPhase(e,t,n,r){let i=e.spinner?.(t,r);i||e.message(t);try{return await n()}finally{i?.stop()}}export{withPhase};
@@ -1,5 +1,5 @@
1
1
  export { type ConnectionSelectOption } from "./connection-add-prompter.js";
2
- export { type ChannelSetupAction, type ChannelSetupAwaitChoice, type ChannelSetupChoice, type ChannelSetupChoiceOptions, type ChannelSetupLog, type DisabledChannelReasons, withPhase, } from "./channel-setup-prompter.js";
2
+ export { type ChannelSetupAction, type ChannelSetupAwaitChoice, type ChannelSetupChoice, type ChannelSetupChoiceOptions, type ChannelSetupLog, type DisabledChannelReasons, type SetupSpinnerIntent, withPhase, } from "./channel-setup-prompter.js";
3
3
  export { createPromptCommandOutput, type PromptCommandLog } from "./command-output.js";
4
4
  export { runSelectComponent, SelectComponent, type SelectGuard } from "./select-component.js";
5
5
  export { createSelectOptionCodec, type SelectOptionCodec } from "./select-option-codec.js";
@@ -21,6 +21,8 @@ export interface PromptColors {
21
21
  export interface PromptOption<T extends PromptValue> {
22
22
  value: T;
23
23
  label: string;
24
+ /** Completion action kept after searchable results instead of being filtered. */
25
+ trailingAction?: boolean;
24
26
  /** Supporting copy; stacked prompts render newline-separated text on separate rows. */
25
27
  hint?: string;
26
28
  /** Short inline annotation shown dimmed only while the cursor is on this row. */
@@ -1 +1 @@
1
- import{previousGraphemeBoundary}from"#shared/text-boundaries.js";const SEARCH_ACTION_PREFIX=`\0search-action:`;function searchActionValue(e){return`${SEARCH_ACTION_PREFIX}${e}`}function searchActionQuery(e){return e.startsWith(SEARCH_ACTION_PREFIX)?e.slice(15):void 0}function submitRowIndex(e){return e.length}function filterOptions(e,t,n){let r=t.trim();if(r===``)return[...e];let i=r.toLowerCase(),a=e.filter(e=>e.label.toLowerCase().includes(i)||e.value.toLowerCase().includes(i)||(e.hint?.toLowerCase().includes(i)??!1)||(e.focusHint?.toLowerCase().includes(i)??!1));return n===void 0?a:[...a,{value:searchActionValue(r),label:n.label(r)}]}function isFocusable(e){return!e.disabled&&!e.locked}function isActionable(e){return isFocusable(e)&&!e.completed}function firstFocusableIndex(e,t){let n=e.findIndex(isFocusable);return n>=0?n:t?submitRowIndex(e):0}function stepCursor(e,t,n,r){let i=e.length+ +!!r;if(i===0)return t;let a=t;for(let t=0;t<i;t+=1){if(a=(a+n+i)%i,r&&a===submitRowIndex(e))return a;let t=e[a];if(t&&isFocusable(t))return a}return t}function reduceSelect(t,n,r){let i=r.submitRow===!0;switch(n.type){case`char`:{let e=t.filter+n.char;return{...t,filter:e,cursor:firstFocusableIndex(filterOptions(r.options,e,r.searchAction),i)}}case`backspace`:{if(t.filter.length===0)return t;let n=t.filter.slice(0,previousGraphemeBoundary(t.filter,t.filter.length));return{...t,filter:n,cursor:firstFocusableIndex(filterOptions(r.options,n,r.searchAction),i)}}case`clear`:return t.filter.length===0?t:{...t,filter:``,cursor:firstFocusableIndex(filterOptions(r.options,``,r.searchAction),i)};case`up`:case`down`:{let e=filterOptions(r.options,t.filter,r.searchAction),a=n.type===`up`?-1:1,o=stepCursor(e,t.cursor,a,i);return o===t.cursor?t:{...t,cursor:o}}case`toggle`:{let e=filterOptions(r.options,t.filter,r.searchAction)[t.cursor];if(e===void 0||!isActionable(e))return t;let n=new Set(t.selected);return n.has(e.value)?n.delete(e.value):n.add(e.value),{...t,selected:n}}}}function initialSelectState(e){let t=e.filter??``,n=filterOptions(e.options,t,e.searchAction),r=firstFocusableIndex(n,e.submitRow===!0);if(e.defaultValue!==void 0){let t=n.findIndex(t=>isFocusable(t)&&t.value===e.defaultValue);t>=0&&(r=t)}let i=e.options.filter(e=>e.locked).map(e=>e.value);return{filter:t,cursor:r,selected:new Set([...e.initialValues??[],...i])}}function selectValueAtCursor(e,t){let n=e[t];return n&&isActionable(n)?n.value:void 0}function orderedSelection(e,t){return e.filter(e=>t.has(e.value)).map(e=>e.value)}export{filterOptions,initialSelectState,orderedSelection,reduceSelect,searchActionQuery,searchActionValue,selectValueAtCursor,submitRowIndex};
1
+ import{previousGraphemeBoundary}from"#shared/text-boundaries.js";const SEARCH_ACTION_PREFIX=`\0search-action:`;function searchActionValue(e){return`${SEARCH_ACTION_PREFIX}${e}`}function searchActionQuery(e){return e.startsWith(SEARCH_ACTION_PREFIX)?e.slice(15):void 0}function submitRowIndex(e){return e.length}function filterOptions(e,t,n){let r=t.trim();if(r===``)return[...e];let i=r.toLowerCase(),a=e.filter(e=>e.trailingAction!==!0&&(e.label.toLowerCase().includes(i)||e.value.toLowerCase().includes(i)||(e.hint?.toLowerCase().includes(i)??!1)||(e.focusHint?.toLowerCase().includes(i)??!1)));return n!==void 0&&a.push({value:searchActionValue(r),label:n.label(r)}),[...a,...e.filter(e=>e.trailingAction===!0)]}function isFocusable(e){return!e.disabled&&!e.locked}function isActionable(e){return isFocusable(e)&&!e.completed}function firstFocusableIndex(e,t){let n=e.findIndex(isFocusable);return n>=0?n:t?submitRowIndex(e):0}function stepCursor(e,t,n,r){let i=e.length+ +!!r;if(i===0)return t;let a=t;for(let t=0;t<i;t+=1){if(a=(a+n+i)%i,r&&a===submitRowIndex(e))return a;let t=e[a];if(t&&isFocusable(t))return a}return t}function reduceSelect(t,n,r){let i=r.submitRow===!0;switch(n.type){case`char`:{let e=t.filter+n.char;return{...t,filter:e,cursor:firstFocusableIndex(filterOptions(r.options,e,r.searchAction),i)}}case`backspace`:{if(t.filter.length===0)return t;let n=t.filter.slice(0,previousGraphemeBoundary(t.filter,t.filter.length));return{...t,filter:n,cursor:firstFocusableIndex(filterOptions(r.options,n,r.searchAction),i)}}case`clear`:return t.filter.length===0?t:{...t,filter:``,cursor:firstFocusableIndex(filterOptions(r.options,``,r.searchAction),i)};case`up`:case`down`:{let e=filterOptions(r.options,t.filter,r.searchAction),a=n.type===`up`?-1:1,o=stepCursor(e,t.cursor,a,i);return o===t.cursor?t:{...t,cursor:o}}case`toggle`:{let e=filterOptions(r.options,t.filter,r.searchAction)[t.cursor];if(e===void 0||!isActionable(e))return t;let n=new Set(t.selected);return n.has(e.value)?n.delete(e.value):n.add(e.value),{...t,selected:n}}}}function initialSelectState(e){let t=e.filter??``,n=filterOptions(e.options,t,e.searchAction),r=firstFocusableIndex(n,e.submitRow===!0);if(e.defaultValue!==void 0){let t=n.findIndex(t=>isFocusable(t)&&t.value===e.defaultValue);t>=0&&(r=t)}let i=e.options.filter(e=>e.locked).map(e=>e.value);return{filter:t,cursor:r,selected:new Set([...e.initialValues??[],...i])}}function selectValueAtCursor(e,t){let n=e[t];return n&&isActionable(n)?n.value:void 0}function orderedSelection(e,t){return e.filter(e=>t.has(e.value)).map(e=>e.value)}export{filterOptions,initialSelectState,orderedSelection,reduceSelect,searchActionQuery,searchActionValue,selectValueAtCursor,submitRowIndex};
@@ -1,66 +1,40 @@
1
1
  import { type ChannelSetupLog } from "#setup/cli/index.js";
2
- /** Controls connector provisioning while adding a Connect-backed connection. */
2
+ import type { Prompter } from "#setup/prompter.js";
3
+ import { type VercelProjectReference } from "#setup/project-resolution.js";
4
+ /** Controls connector selection while adding a Connect-backed connection. */
3
5
  export interface SetupConnectionConnectorOptions {
4
- /** Status and command output stream through this log (rail styling preserved). */
5
6
  log: ChannelSetupLog;
7
+ prompter: Prompter;
6
8
  projectRoot: string;
7
- /** Connection slug; also the connector `--name`. */
8
9
  slug: string;
9
- /** `vercel connect create <service>` identifier (e.g. `mcp.linear.app`). */
10
10
  service: string;
11
- /** Generated `agent/connections/<slug>.ts` whose `connect("…")` is patched. */
12
- connectionFilePath: string;
13
- /**
14
- * Links a Vercel project before Connect provisioning when the caller owns a
15
- * richer linking flow (e.g. shared team selection). Returns the linked
16
- * project id, or `undefined` when linking did not complete. When omitted,
17
- * falls back to a bare `vercel link`.
18
- */
19
- linkProject?: () => Promise<string | undefined>;
11
+ canonicalConnectorName: string;
12
+ project: VercelProjectReference;
13
+ signal?: AbortSignal;
14
+ }
15
+ /** Connector identity returned by the Vercel CLI. */
16
+ export interface ConnectConnectorRef {
17
+ uid: string;
18
+ id: string;
19
+ name?: string;
20
20
  }
21
- /** Outcome of the Connect create-and-patch sequence for a connection. */
22
21
  export type SetupConnectionConnectorResult = {
23
- kind: "create-failed";
24
- created: false;
25
- } | {
26
- kind: "connector-unresolved";
27
- created: true;
28
- } | {
29
- kind: "patch-failed";
30
- created: true;
22
+ kind: "existing";
31
23
  connectorUid: string;
32
24
  } | {
33
- kind: "patched";
34
- created: true;
25
+ kind: "created";
35
26
  connectorUid: string;
27
+ connectorId: string;
36
28
  };
37
- /** Identifiers returned by Vercel Connect for an OAuth connector. */
38
- export interface ConnectConnectorRef {
39
- uid: string;
40
- id: string;
41
- }
42
- /**
43
- * Reads the connector identifiers from `vercel connect create … -F json`
44
- * stdout. This is the authoritative source for the just-created connector's
45
- * UID — it avoids a follow-up `connect list`, which can momentarily 404/rate
46
- * limit right after creation and cannot disambiguate when a service already
47
- * has multiple connectors. Returns `undefined` when stdout is empty or not the
48
- * expected JSON (e.g. an older CLI without `-F json` support on `create`).
49
- */
29
+ /** Parses a created connector that can issue user credentials. */
50
30
  export declare function parseCreatedConnector(stdout: string): ConnectConnectorRef | undefined;
51
- /**
52
- * Finds the connector to wire into the generated connection. The list is
53
- * expected to be scoped to the requested service server-side (via
54
- * `--service`), since `vercel connect list -F json` does not include a
55
- * `service` field per connector. Prefers, in order: the connector already
56
- * attached to this project, then the newest connector. When Connect does
57
- * report a `service` field, mismatches are still skipped defensively.
58
- */
59
- export declare function pickConnectConnector(listJson: unknown, service: string, projectId: string | undefined): ConnectConnectorRef | undefined;
60
- /**
61
- * Creates a Vercel Connect OAuth connector for a connection and rewrites the
62
- * generated `connect("…")` call to the connector UID Connect assigns. The
63
- * `vercel connect create` step is interactive (it opens a browser to complete
64
- * the OAuth grant); callers should only invoke this from an interactive flow.
65
- */
31
+ /** Removes a connector created by this setup attempt. */
32
+ export declare function cleanupCreatedConnectionConnector(input: {
33
+ log: ChannelSetupLog;
34
+ projectRoot: string;
35
+ connectorId: string;
36
+ /** The linked Vercel owner; inferred from the project link when omitted. */
37
+ orgId?: string;
38
+ }): Promise<void>;
39
+ /** Attaches the canonical connector by name first, then offers explicit Find/Create fallbacks. */
66
40
  export declare function setupConnectionConnector(options: SetupConnectionConnectorOptions): Promise<SetupConnectionConnectorResult>;
@@ -1 +1,2 @@
1
- import{join}from"node:path";import{readFile}from"node:fs/promises";import{createPromptCommandOutput}from"#setup/cli/index.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{updateConnectionConnectorUid}from"#setup/scaffold/update/update-connection-connector.js";function parseCreatedConnector(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 attachedToProject(e,t){return t===void 0||!Array.isArray(e.projects)?!1:e.projects.some(e=>typeof e==`object`&&!!e&&e.id===t)}function pickConnectConnector(e,t,n){if(typeof e!=`object`||!e)return;let r=e,i=r.connectors??r.clients;if(!Array.isArray(i))return;let a,o;for(let e of i){if(typeof e.service==`string`&&e.service!==t||typeof e.uid!=`string`||typeof e.id!=`string`)continue;let r={uid:e.uid,id:e.id},i=typeof e.createdAt==`number`?e.createdAt:0;(!o||i>o.createdAt)&&(o={ref:r,createdAt:i}),attachedToProject(e,n)&&(!a||i>a.createdAt)&&(a={ref:r,createdAt:i})}return(a??o)?.ref}async function readProjectId(n){try{let r=await readFile(join(n,`.vercel`,`project.json`),`utf8`),i=JSON.parse(r);return typeof i.projectId==`string`?i.projectId:void 0}catch{return}}async function ensureLinkedProject(e,t,n){return await readProjectId(t)||(e.message(`Linking a Vercel project for Connect...`),await runVercel([`link`],{cwd:t,onOutput:n}),readProjectId(t))}async function findConnector(e,t,n,i){let a=await captureVercel([`connect`,`list`,`-F`,`json`,`--all-projects`,`--service`,t],{cwd:e,onOutput:i});if(a.ok)try{return pickConnectConnector(JSON.parse(a.stdout),t,n)}catch{return}}async function setupConnectionConnector(e){let{log:t,projectRoot:r,slug:s,service:c,connectionFilePath:l}=e,u=createPromptCommandOutput(t),d=e.linkProject?await e.linkProject():await ensureLinkedProject(t,r,u);t.message(`Connecting ${s} via Vercel Connect...`);let f=await runVercelCaptureStdout([`connect`,`create`,c,`--name`,s,`-F`,`json`],{cwd:r,onOutput:u});if(!f.ok)return t.warning(`Could not create the connector. Run \`vercel connect create ${c} --name ${s}\`, then set the UID in agent/connections/${s}.ts.`),{kind:`create-failed`,created:!1};let p=parseCreatedConnector(f.stdout);if(p||=await findConnector(r,c,d,u),!p)return t.warning(`Could not locate the connector. Run \`vercel connect list --all-projects\` to find its UID, then set it in agent/connections/${s}.ts.`),{kind:`connector-unresolved`,created:!0};d?await runVercel([`connect`,`attach`,p.uid,`--yes`],{cwd:r,onOutput:u})||t.warning(`Created connector ${p.uid} but could not attach it to this project. Run \`vercel connect attach ${p.uid} --yes\`.`):t.warning(`Created connector ${p.uid} but no Vercel project is linked, so it isn't attached. Run \`vercel link\`, then \`vercel connect attach ${p.uid} --yes\`.`);let{patched:m}=await updateConnectionConnectorUid(l,p.uid);return m?(t.success(`Linked ${s} to ${p.uid}`),{kind:`patched`,created:!0,connectorUid:p.uid}):(t.warning(`Created connector ${p.uid}. Update \`connect("…")\` in agent/connections/${s}.ts to "${p.uid}".`),{kind:`patch-failed`,created:!0,connectorUid:p.uid})}export{parseCreatedConnector,pickConnectConnector,setupConnectionConnector};
1
+ import{readProjectLink}from"#setup/project-resolution.js";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";const CREATED_CONNECTOR=/\bConnector created:\s*(scl_[A-Za-z0-9_-]+)\b/u;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function parseJson(e){try{return JSON.parse(e)}catch{return}}function parseConnectorRef(e){if(!isRecord(e)||typeof e.uid!=`string`||typeof e.id!=`string`)return;let t={uid:e.uid,id:e.id};return typeof e.name==`string`&&(t.name=e.name),t}function parseCreatedConnector(e){let t=parseJson(e),n=parseConnectorRef(t);if(!isRecord(t)||n===void 0)return;let r=t.supportedSubjectTypes;return Array.isArray(r)&&r.includes(`user`)?n:void 0}function parseConnectorList(e,t){let n=[];for(let r of e){if(!isRecord(r)||typeof r.service==`string`&&r.service!==t)continue;let e=parseConnectorRef(r);e!==void 0&&n.push(e)}return n}function parseConnectorListPage(e,t){if(!isRecord(e)||!Array.isArray(e.connectors))return;let n=typeof e.cursor==`string`?e.cursor:void 0;return n===void 0?{connectors:parseConnectorList(e.connectors,t)}:{connectors:parseConnectorList(e.connectors,t),cursor:n}}async function listConnectors(e,t,n){let i=[],a=new Set,o;do{let s=[`connect`,`list`,`-F`,`json`,`--all-projects`,`--service`,e.service,`--scope`,t.orgId];o!==void 0&&s.push(`--next`,o);let c=await captureVercel(s,{cwd:e.projectRoot,onOutput:n,signal:e.signal});if(!c.ok)throw Error(c.failure.message);let l=parseConnectorListPage(parseJson(c.stdout),e.service);if(l===void 0)throw Error(`Vercel returned an invalid connector list for ${e.service}.`);i.push(...l.connectors);let u=l.cursor;if(u!==void 0&&a.has(u))throw Error(`The connector list repeated cursor ${u}.`);u!==void 0&&a.add(u),o=u}while(o!==void 0);return i}async function supportsUserAuthorization(e,t,n,i){let a=await captureVercel([`api`,`/v1/connect/connectors/${encodeURIComponent(n.id)}`,`--scope`,t.orgId,`--raw`],{cwd:e.projectRoot,onOutput:i,signal:e.signal});if(!a.ok)throw Error(`Could not verify connector ${n.uid}.`);let o=parseJson(a.stdout);if(!isRecord(o)||o.id!==n.id||o.uid!==n.uid||typeof o.service==`string`&&o.service!==e.service)throw Error(`Vercel returned invalid details for connector ${n.uid}.`);let s=o.supportedSubjectTypes;return Array.isArray(s)&&s.includes(`user`)}function connectorNames(e){let t=new Set;for(let n of e){n.name!==void 0&&t.add(n.name.toLowerCase());let e=n.uid.slice(n.uid.lastIndexOf(`/`)+1).trim();e.length>0&&t.add(e.toLowerCase())}return t}function connectorMatchesCanonicalName(e,t){let n=t.toLowerCase();return e.name?.toLowerCase()===n?!0:e.uid.slice(e.uid.lastIndexOf(`/`)+1).trim().toLowerCase()===n}function nextConnectorName(e,t){if(!t.has(e.toLowerCase()))return e;let n=2;for(;t.has(`${e}-${n}`.toLowerCase());)n+=1;return`${e}-${n}`}async function cleanupCreatedConnectionConnector(n){let r=n.orgId??(await readProjectLink(n.projectRoot))?.orgId,a=[`connect`,`remove`,n.connectorId,`--disconnect-all`,`--yes`];if(r!==void 0&&a.push(`--scope`,r),!await runVercel(a,{cwd:n.projectRoot,onOutput:createPromptCommandOutput(n.log)}))throw Error(`Could not remove connector ${n.connectorId}; run \`vercel connect remove ${n.connectorId} --disconnect-all --yes\`.`)}async function attach(e,t,n,r){return runVercel([`connect`,`attach`,n,`--yes`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:r,signal:e.signal})}async function resolveFallbackConnector(e,t,r,i,s){let c=s;for(;;){if(await e.prompter.select({message:`Which connector should ${e.slug} use?`,hintLayout:`inline`,notices:[{tone:`warning`,text:c}],options:[{value:`find`,label:`Find a new one`,hint:`Browse existing connectors`},{value:`create`,label:`Create a new one`,hint:`Register another connector`}]})===`find`){let n=[];for(let a of i)await supportsUserAuthorization(e,t,a,r)&&n.push(a);if(n.length===0){c=`No existing ${e.service} connectors support user authorization.`;continue}let a=new Map(n.map(e=>[e.uid,e])),o=await e.prompter.select({message:`Select a connector for ${e.slug}`,hintLayout:`inline`,search:!0,placeholder:`type to search connectors`,options:n.map(e=>({value:e.uid,label:e.uid,hint:e.name??e.id}))}),s=a.get(o);if(s===void 0)throw Error(`Connector ${o} is no longer available.`);return{kind:`existing`,connector:s}}let s=connectorNames(i),l=(await e.prompter.text({message:`New connector name`,defaultValue:nextConnectorName(e.slug,s),validate:e=>{let t=e.trim().toLowerCase();return t.length===0?`A name is required.`:s.has(t)?`A connector with this name already exists.`:void 0}})).trim(),u=[],createOutput=e=>{u.push(e.text),r(e)},d=await withPhase(e.log,`Waiting for you to complete setup in the browser…`,()=>runVercelCaptureStdout([`connect`,`create`,e.service,`--name`,l,`-F`,`json`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:createOutput,signal:e.signal}),{kind:`external-action`,emphasis:`browser`}),f=parseConnectorRef(parseJson(d.stdout))?.id??CREATED_CONNECTOR.exec(u.join(`
2
+ `))?.[1],p=d.ok?parseCreatedConnector(d.stdout):void 0;if(p!==void 0)return{kind:`created`,connector:p};let m=d.ok?`The ${e.service} connector does not support user authorization.`:`Could not create the ${e.service} connector.`;if(f!==void 0){try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:f,orgId:t.orgId})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`${m} ${t}`)}e.signal?.throwIfAborted()}throw Error(m)}}async function setupConnectionConnector(e){let n=createPromptCommandOutput(e.log),r=e.project,i=await listConnectors(e,r,n),a=i.find(t=>connectorMatchesCanonicalName(t,e.canonicalConnectorName)),o=`Could not find a connector named ${e.canonicalConnectorName}.`;if(a!==void 0)if(await supportsUserAuthorization(e,r,a,n)){if(await attach(e,r,a.uid,n))return e.log.success(`Attached ${a.uid} connector`),{kind:`existing`,connectorUid:a.uid};e.signal?.throwIfAborted(),o=`Could not attach ${a.uid}.`}else o=`${a.uid} does not support user authorization.`;let s=await resolveFallbackConnector(e,r,n,i,o);if(!await attach(e,r,s.connector.uid,n)){if(s.kind===`created`){let t=`Could not attach ${s.connector.uid} to the linked project.`;try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:s.connector.id,orgId:r.orgId})}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`${t} ${n}`)}throw e.signal?.throwIfAborted(),Error(t)}throw Error(`Could not attach ${s.connector.uid} to the linked project.`)}return e.log.success(`Attached ${s.connector.uid} connector`),s.kind===`created`?{kind:`created`,connectorUid:s.connector.uid,connectorId:s.connector.id}:{kind:`existing`,connectorUid:s.connector.uid}}export{cleanupCreatedConnectionConnector,parseCreatedConnector,setupConnectionConnector};
@@ -1 +1 @@
1
- import{interactiveAsker}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{createDefaultSetupState,snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{addChannels}from"../boxes/add-channels.js";import{withSpinner}from"../with-spinner.js";import{getVercelAuthStatus,vercelAuthBlockerReason}from"../vercel-project.js";import{CHANNELS_PROMPT_MESSAGE,selectChannels}from"../boxes/select-channels.js";import{assertCanAddSelectedChannels,inspectExistingChannelRegistrations}from"../channel-add-conflicts.js";import{runInteractive}from"../runner.js";import{prompterSink}from"./in-project.js";import{SCAFFOLDABLE_CHANNELS}from"#setup/scaffold/index.js";import{toErrorMessage}from"#shared/errors.js";const SEE_IT_LIVE_MESSAGE=`See it live`;async function promptSeeItLive(e){try{return await e.select({message:SEE_IT_LIVE_MESSAGE,options:[{value:`deploy`,label:`Deploy and chat`},{value:`later`,label:`Later`}]})}catch(e){if(e instanceof WizardCancelledError)return`later`;throw e}}function channelAlreadyAdded(e,t){return t===`web`?e.webAppPresent:e.slackOwners.length>0}function appendChannel(e,t){return e.includes(t)?[...e]:[...e,t]}async function pickChannel(e,t,n,r){let i=channelListRows(t,n,r),a=!i.some(e=>e.value!==`done`&&e.completed!==!0&&e.disabled!==!0),s={message:CHANNELS_PROMPT_MESSAGE,options:i,hintLayout:`inline`};a&&(s.initialValue=`done`);try{return{kind:`picked`,value:await e.select(s)}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}function channelLandedDuringSubflow(e,t,n){return!channelAlreadyAdded(e,n)&&channelAlreadyAdded(t,n)}function deployAndChatDetails(e){let t={};return e.slackChatUrl!==void 0&&(t.chatUrl=e.slackChatUrl),e.slackWorkspaceName!==void 0&&(t.workspaceName=e.slackWorkspaceName),t}function vercelChannelBlocker(e,t){let n=vercelAuthBlockerReason(e);if(n!==void 0)return n;if(!t)return`Requires Vercel account, see /model`}function channelListRows(e,t,n){let r=[{value:`repl`,label:`Terminal UI`,completed:!0,focusHint:`Already installed`}];for(let i of SCAFFOLDABLE_CHANNELS){if(channelAlreadyAdded(e,i.kind)){r.push({value:i.kind,label:i.label,completed:!0,focusHint:`Already installed`});continue}let a=e.disabledChannelReasons[i.kind];if(a!==void 0){r.push({value:i.kind,label:i.label,disabled:!0,disabledReason:a});continue}if(i.requiresVercelProject===!0){let e=vercelChannelBlocker(n,t);if(e!==void 0){r.push({value:i.kind,label:i.label,disabled:!0,disabledReason:e,disabledReasonTone:`warning`});continue}}let o={value:i.kind,label:i.label};i.hint!==void 0&&(o.hint=i.hint),r.push(o)}return r.push({value:`done`,label:`Done`}),r}async function runChannelsFlow(o){let{appRoot:c,prompter:l,signal:u}=o,d={detectDeployment,inspectExistingChannelRegistrations,getVercelAuthStatus,...o.deps},[f,p,m]=await withSpinner(l,`Checking the project…`,()=>Promise.all([d.detectDeployment(c,{signal:u}),d.inspectExistingChannelRegistrations(c),d.getVercelAuthStatus(c,{signal:u})]));u?.throwIfAborted();let h=p,g={...createDefaultSetupState(),project:projectResolutionFromDeployment(f),projectPath:{kind:`resolved`,inPlace:!0,path:c}},_;for(;;){let t=await pickChannel(l,h,isProjectResolved(g.project),m);if(t.kind===`cancelled`){if(g.channels.length===0)return{kind:`cancelled`};break}let r=t.value;if(r===`done`)break;if(r===`repl`||channelAlreadyAdded(h,r))continue;let i=[selectChannels({asker:interactiveAsker(l),variant:`channels-add`,presetChannels:[r],validateSelection:e=>assertCanAddSelectedChannels(e,h)}),addChannels({asker:interactiveAsker(l),prompter:l,configureVercelServices:!0,deps:d.addChannels})],o;try{o=await runInteractive(i,g,prompterSink(l),{snapshot:snapshotSetupState,signal:u})}catch(e){let t=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c));if(channelLandedDuringSubflow(h,t,r)){if(g={...g,channels:appendChannel(g.channels,r)},h=t,_=toErrorMessage(e),u?.aborted===!0)break;continue}throw e}if(o.kind===`done`){if(g=o.state,h=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c)),u?.throwIfAborted(),r===`slack`&&g.slackbotAttached&&await promptSeeItLive(l)===`deploy`)return{kind:`deploy-and-chat`,addedChannels:g.channels,chat:deployAndChatDetails(g)}}else{let e=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c));if(channelLandedDuringSubflow(h,e,r))return{kind:`done`,addedChannels:appendChannel(g.channels,r)};h=e}}return _===void 0?{kind:`done`,addedChannels:g.channels}:{kind:`failed`,addedChannels:g.channels,message:_}}export{SEE_IT_LIVE_MESSAGE,runChannelsFlow};
1
+ import{interactiveAsker}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{createDefaultSetupState,snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{addChannels}from"../boxes/add-channels.js";import{withSpinner}from"../with-spinner.js";import{getVercelAuthStatus,vercelAuthBlockerReason}from"../vercel-project.js";import{CHANNELS_PROMPT_MESSAGE,selectChannels}from"../boxes/select-channels.js";import{assertCanAddSelectedChannels,inspectExistingChannelRegistrations}from"../channel-add-conflicts.js";import{runInteractive}from"../runner.js";import{prompterSink}from"./in-project.js";import{SCAFFOLDABLE_CHANNELS}from"#setup/scaffold/index.js";import{toErrorMessage}from"#shared/errors.js";const SEE_IT_LIVE_MESSAGE=`See it live`;async function promptSeeItLive(e){try{return await e.select({message:SEE_IT_LIVE_MESSAGE,options:[{value:`deploy`,label:`Deploy and chat`},{value:`later`,label:`Later`}]})}catch(e){if(e instanceof WizardCancelledError)return`later`;throw e}}function channelAlreadyAdded(e,t){return t===`web`?e.webAppPresent:e.slackOwners.length>0}function appendChannel(e,t){return e.includes(t)?[...e]:[...e,t]}async function pickChannel(e,t,n,r){let i=channelListRows(t,n,r),a=!i.some(e=>e.value!==`done`&&e.completed!==!0&&e.disabled!==!0),s={message:CHANNELS_PROMPT_MESSAGE,options:i,hintLayout:`inline`};a&&(s.initialValue=`done`);try{return{kind:`picked`,value:await e.select(s)}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}function channelLandedDuringSubflow(e,t,n){return!channelAlreadyAdded(e,n)&&channelAlreadyAdded(t,n)}function deployAndChatDetails(e){let t={};return e.slackChatUrl!==void 0&&(t.chatUrl=e.slackChatUrl),e.slackWorkspaceName!==void 0&&(t.workspaceName=e.slackWorkspaceName),t}function vercelChannelBlocker(e,t){let n=vercelAuthBlockerReason(e);if(n!==void 0)return n;if(!t)return`Requires Vercel account, see /model`}function channelListRows(e,t,n){let r=[{value:`repl`,label:`Terminal UI`,completed:!0,focusHint:`Already installed`}];for(let i of SCAFFOLDABLE_CHANNELS){if(channelAlreadyAdded(e,i.kind)){r.push({value:i.kind,label:i.label,completed:!0,focusHint:`Already installed`});continue}let a=e.disabledChannelReasons[i.kind];if(a!==void 0){r.push({value:i.kind,label:i.label,disabled:!0,disabledReason:a});continue}if(i.requiresVercelProject===!0){let e=vercelChannelBlocker(n,t);if(e!==void 0){r.push({value:i.kind,label:i.label,disabled:!0,disabledReason:e,disabledReasonTone:`warning`});continue}}let o={value:i.kind,label:i.label};i.hint!==void 0&&(o.hint=i.hint),r.push(o)}return r.push({value:`done`,label:`Done`,trailingAction:!0}),r}async function runChannelsFlow(o){let{appRoot:c,prompter:l,signal:u}=o,d={detectDeployment,inspectExistingChannelRegistrations,getVercelAuthStatus,...o.deps},[f,p,m]=await withSpinner(l,`Checking the project…`,()=>Promise.all([d.detectDeployment(c,{signal:u}),d.inspectExistingChannelRegistrations(c),d.getVercelAuthStatus(c,{signal:u})]));u?.throwIfAborted();let h=p,g={...createDefaultSetupState(),project:projectResolutionFromDeployment(f),projectPath:{kind:`resolved`,inPlace:!0,path:c}},_;for(;;){let t=await pickChannel(l,h,isProjectResolved(g.project),m);if(t.kind===`cancelled`){if(g.channels.length===0)return{kind:`cancelled`};break}let r=t.value;if(r===`done`)break;if(r===`repl`||channelAlreadyAdded(h,r))continue;let i=[selectChannels({asker:interactiveAsker(l),variant:`channels-add`,presetChannels:[r],validateSelection:e=>assertCanAddSelectedChannels(e,h)}),addChannels({asker:interactiveAsker(l),prompter:l,configureVercelServices:!0,deps:d.addChannels})],o;try{o=await runInteractive(i,g,prompterSink(l),{snapshot:snapshotSetupState,signal:u})}catch(e){let t=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c));if(channelLandedDuringSubflow(h,t,r)){if(g={...g,channels:appendChannel(g.channels,r)},h=t,_=toErrorMessage(e),u?.aborted===!0)break;continue}throw e}if(o.kind===`done`){if(g=o.state,h=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c)),u?.throwIfAborted(),r===`slack`&&g.slackbotAttached&&await promptSeeItLive(l)===`deploy`)return{kind:`deploy-and-chat`,addedChannels:g.channels,chat:deployAndChatDetails(g)}}else{let e=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c));if(channelLandedDuringSubflow(h,e,r))return{kind:`done`,addedChannels:appendChannel(g.channels,r)};h=e}}return _===void 0?{kind:`done`,addedChannels:g.channels}:{kind:`failed`,addedChannels:g.channels,message:_}}export{SEE_IT_LIVE_MESSAGE,runChannelsFlow};
@@ -0,0 +1,36 @@
1
+ import { ensureConnectionDependencies, listAuthoredConnections } from "#setup/scaffold/index.js";
2
+ import { detectPackageManager } from "#setup/package-manager.js";
3
+ import { runPackageManagerInstall } from "#setup/primitives/pm/run.js";
4
+ import { type AddConnectionsDeps } from "../boxes/add-connections.js";
5
+ import { detectDeployment } from "../project-resolution.js";
6
+ import type { Prompter } from "../prompter.js";
7
+ import { getVercelAuthStatus } from "../vercel-project.js";
8
+ import { runLinkFlow } from "./link.js";
9
+ export declare const CONNECTIONS_PROMPT_MESSAGE = "Select an MCP server to add to your agent through Vercel Connect";
10
+ export interface ConnectionsFlowDeps {
11
+ detectDeployment: typeof detectDeployment;
12
+ detectPackageManager: typeof detectPackageManager;
13
+ getVercelAuthStatus: typeof getVercelAuthStatus;
14
+ runLinkFlow: typeof runLinkFlow;
15
+ ensureConnectionDependencies: typeof ensureConnectionDependencies;
16
+ listAuthoredConnections: typeof listAuthoredConnections;
17
+ runPackageManagerInstall: typeof runPackageManagerInstall;
18
+ addConnections?: AddConnectionsDeps;
19
+ }
20
+ export type ConnectionsFlowResult = {
21
+ kind: "done";
22
+ addedConnections: readonly string[];
23
+ } | {
24
+ kind: "cancelled";
25
+ } | {
26
+ kind: "failed";
27
+ addedConnections: readonly string[];
28
+ message: string;
29
+ };
30
+ /** Runs `/connect`, linking a project on first selection when needed. */
31
+ export declare function runConnectionsFlow(input: {
32
+ appRoot: string;
33
+ prompter: Prompter;
34
+ signal?: AbortSignal;
35
+ deps?: Partial<ConnectionsFlowDeps>;
36
+ }): Promise<ConnectionsFlowResult>;
@@ -0,0 +1,3 @@
1
+ import{interactiveAsker}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{withSpinner}from"../with-spinner.js";import{getVercelAuthStatus,vercelAuthBlockerReason}from"../vercel-project.js";import{selectConnections}from"../boxes/select-connections.js";import{addConnections}from"../boxes/add-connections.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";import{runLinkFlow}from"./link.js";import{CONNECTION_CATALOG,ensureConnectionDependencies,listAuthoredConnections}from"#setup/scaffold/index.js";import{detectPackageManager}from"#setup/package-manager.js";import{toErrorMessage}from"#shared/errors.js";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{runPackageManagerInstall}from"#setup/primitives/pm/run.js";const CONNECTIONS_PROMPT_MESSAGE=`Select an MCP server to add to your agent through Vercel Connect`,CONNECT_CONNECTIONS=CONNECTION_CATALOG.filter(e=>e.auth.kind===`connect`);function connectionRows(e,t){let n=vercelAuthBlockerReason(t),r=CONNECT_CONNECTIONS.map(t=>{let r={value:t.slug,label:t.label};return e.has(t.slug)?{...r,completed:!0,focusHint:`Already added`}:n===void 0?{...r,hint:t.hint}:{...r,disabled:!0,disabledReason:n,disabledReasonTone:`warning`}});return r.push({value:`done`,label:`Done`,trailingAction:!0}),r}async function pickConnection(e,t,n){let r=connectionRows(t,n),i={message:CONNECTIONS_PROMPT_MESSAGE,options:r,hintLayout:`inline`,search:!0,placeholder:`type to search MCP servers`};r.some(e=>e.value!==`done`&&e.disabled!==!0&&e.completed!==!0)||(i.initialValue=`done`);try{return await e.select(i)}catch(e){if(e instanceof WizardCancelledError)return;throw e}}async function runConnectionsFlow(a){let{appRoot:o,prompter:s,signal:c}=a,l={detectDeployment,detectPackageManager,ensureConnectionDependencies,getVercelAuthStatus,listAuthoredConnections,runLinkFlow,runPackageManagerInstall,...a.deps},[u,d,f]=await withSpinner(s,`Checking the project…`,()=>Promise.all([l.detectDeployment(o,{signal:c}),l.listAuthoredConnections(o),l.getVercelAuthStatus(o,{signal:c})]));c?.throwIfAborted();let p=inProjectSetupState(o,projectResolutionFromDeployment(u)),m=new Set(d),h=await pickConnection(s,m,f);if(h===void 0)return{kind:`cancelled`};if(h===`done`||m.has(h))return{kind:`done`,addedConnections:[]};try{if(!isProjectResolved(p.project)){if((await l.runLinkFlow({appRoot:o,prompter:s,signal:c,projectSelection:`create-or-link`,teamSelectMessage:()=>`You need to link to a project to use Vercel Connect.
2
+
3
+ Select your team`})).kind===`cancelled`)return{kind:`cancelled`};let e=projectResolutionFromDeployment(await withSpinner(s,`Checking the project…`,()=>l.detectDeployment(o,{signal:c})));if(!isProjectResolved(e))throw Error(`Project link was not found after linking.`);p={...p,project:e}}let t=await l.detectPackageManager(o);if(await l.ensureConnectionDependencies({projectRoot:o}),!await withPhase(s.log,`Installing connection dependencies (${t.kind} install)...`,()=>l.runPackageManagerInstall(t.kind,o,{onOutput:createPromptCommandOutput(s.log),signal:c})))throw Error(`Dependency installation failed. Run \`${t.kind} install\`.`);if((await runInteractive([selectConnections({asker:interactiveAsker(s),presetConnections:[h]}),addConnections({prompter:s,signal:c,deps:l.addConnections})],p,prompterSink(s),{snapshot:snapshotSetupState,signal:c})).kind!==`done`)return{kind:`cancelled`};if(!new Set(await l.listAuthoredConnections(o)).has(h))throw Error(`Connection "${h}" was not added.`);return{kind:`done`,addedConnections:[h]}}catch(e){return e instanceof WizardCancelledError?{kind:`cancelled`}:{kind:`failed`,addedConnections:new Set(await l.listAuthoredConnections(o)).has(h)?[h]:[],message:toErrorMessage(e)}}}export{CONNECTIONS_PROMPT_MESSAGE,runConnectionsFlow};
@@ -47,5 +47,6 @@ export declare function runLinkFlow(input: {
47
47
  * branch, where a fresh agent has no project yet).
48
48
  */
49
49
  projectSelection?: "create-or-link" | "existing-only";
50
+ teamSelectMessage?: (currentTeam: string) => string;
50
51
  deps?: Partial<LinkFlowDeps>;
51
52
  }): Promise<LinkFlowResult>;
@@ -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{AI_GATEWAY_API_KEY_ENV_VAR}from"../ai-gateway-api-key.js";import{interactiveAsker,withAnswers}from"../ask.js";import{detectProjectIdentity}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{withSpinner}from"../with-spinner.js";import{detectAiGateway,findEnvFileWithKey}from"../boxes/detect-ai-gateway.js";import{applyAiGatewayCredential}from"../boxes/apply-ai-gateway-credential.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";var import_picocolors=__toESM(require_picocolors(),1);async function runLinkFlow(e){let{appRoot:t,prompter:i,signal:a,projectSelection:o=`existing-only`}=e,s={detectProjectIdentity,findEnvFileWithKey,...e.deps},c=await withSpinner(i,`Checking the current Vercel link...`,async()=>{let e=await s.detectProjectIdentity(t,{signal:a});return a?.throwIfAborted(),e});if(c===void 0){let[e,n]=await Promise.all([s.findEnvFileWithKey(t,AI_GATEWAY_API_KEY_ENV_VAR),s.findEnvFileWithKey(t,`VERCEL_OIDC_TOKEN`)]),r=e??n;r!==void 0&&i.log.message(`This directory is not linked to a Vercel project yet — the model currently runs on credentials from ${r}.`)}else{let e=c.teamName===void 0?import_picocolors.default.bold(c.projectName):`${import_picocolors.default.bold(c.projectName)} in ${import_picocolors.default.bold(c.teamName)}`;try{if(await i.select({message:`This directory is already linked to\n${e}`,options:[{value:`relink`,label:`Link to another project`},{value:`dismiss`,label:`Dismiss`}]})===`dismiss`)return{kind:`cancelled`}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}let l=inProjectSetupState(t,{kind:`unresolved`});if((await runInteractive([resolveProvisioning({asker:withAnswers({deploy:`vercel`})(interactiveAsker(i)),prompter:i,targetDirectory:t,mode:{headless:!1},adoptExistingLink:!1,projectSelection:o,deps:s.resolveProvisioning}),linkVercelProject({prompter:i,deps:s.linkProject}),detectAiGateway(),applyAiGatewayCredential({prompter:i,deps:s.applyAiGatewayCredential})],l,prompterSink(i),{snapshot:snapshotSetupState,signal:a})).kind===`cancelled`)return{kind:`cancelled`};let[u,d]=await Promise.all([s.findEnvFileWithKey(t,`VERCEL_OIDC_TOKEN`),s.findEnvFileWithKey(t,AI_GATEWAY_API_KEY_ENV_VAR)]);a?.throwIfAborted(),u===void 0&&d===void 0&&i.log.warning("Linked, but no model credential landed in an env file (VERCEL_OIDC_TOKEN or AI_GATEWAY_API_KEY). Run `vercel env pull` once the project has AI Gateway access.");let f={kind:`done`};return u===void 0?d!==void 0&&(f.credential=AI_GATEWAY_API_KEY_ENV_VAR):f.credential=`VERCEL_OIDC_TOKEN`,f}export{runLinkFlow};
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{AI_GATEWAY_API_KEY_ENV_VAR}from"../ai-gateway-api-key.js";import{interactiveAsker,withAnswers}from"../ask.js";import{detectProjectIdentity}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{withSpinner}from"../with-spinner.js";import{detectAiGateway,findEnvFileWithKey}from"../boxes/detect-ai-gateway.js";import{applyAiGatewayCredential}from"../boxes/apply-ai-gateway-credential.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";var import_picocolors=__toESM(require_picocolors(),1);async function runLinkFlow(e){let{appRoot:t,prompter:a,signal:o,projectSelection:s=`existing-only`}=e,c={detectProjectIdentity,findEnvFileWithKey,...e.deps},l=await withSpinner(a,`Checking the current Vercel link...`,async()=>{let e=await c.detectProjectIdentity(t,{signal:o});return o?.throwIfAborted(),e});if(l===void 0){let[n,r]=await Promise.all([c.findEnvFileWithKey(t,AI_GATEWAY_API_KEY_ENV_VAR),c.findEnvFileWithKey(t,`VERCEL_OIDC_TOKEN`)]),i=n??r;i!==void 0&&e.teamSelectMessage===void 0&&a.log.message(`This directory is not linked to a Vercel project yet — the model currently runs on credentials from ${i}.`)}else{let e=l.teamName===void 0?import_picocolors.default.bold(l.projectName):`${import_picocolors.default.bold(l.projectName)} in ${import_picocolors.default.bold(l.teamName)}`;try{if(await a.select({message:`This directory is already linked to\n${e}`,options:[{value:`relink`,label:`Link to another project`},{value:`dismiss`,label:`Dismiss`}]})===`dismiss`)return{kind:`cancelled`}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}let u=inProjectSetupState(t,{kind:`unresolved`});if((await runInteractive([resolveProvisioning({asker:withAnswers({deploy:`vercel`})(interactiveAsker(a)),prompter:a,targetDirectory:t,mode:{headless:!1},adoptExistingLink:!1,projectSelection:s,teamSelectMessage:e.teamSelectMessage,deps:c.resolveProvisioning}),linkVercelProject({prompter:a,deps:c.linkProject}),detectAiGateway(),applyAiGatewayCredential({prompter:a,deps:c.applyAiGatewayCredential})],u,prompterSink(a),{snapshot:snapshotSetupState,signal:o})).kind===`cancelled`)return{kind:`cancelled`};let[d,f]=await Promise.all([c.findEnvFileWithKey(t,`VERCEL_OIDC_TOKEN`),c.findEnvFileWithKey(t,AI_GATEWAY_API_KEY_ENV_VAR)]);o?.throwIfAborted(),d===void 0&&f===void 0&&a.log.warning("Linked, but no model credential landed in an env file (VERCEL_OIDC_TOKEN or AI_GATEWAY_API_KEY). Run `vercel env pull` once the project has AI Gateway access.");let p={kind:`done`};return d===void 0?f!==void 0&&(p.credential=AI_GATEWAY_API_KEY_ENV_VAR):p.credential=`VERCEL_OIDC_TOKEN`,p}export{runLinkFlow};
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { type VercelProjectLink } from "#internal/vercel/project-link.js";
2
2
  /** Link and production-deployment status for a Vercel project directory. */
3
3
  export type DeploymentState = "unlinked" | "linked" | "deployed";
4
4
  /** Vercel project data resolved from local link metadata and the API. */
@@ -8,13 +8,8 @@ export interface DeploymentInfo {
8
8
  orgId?: string;
9
9
  productionUrl?: string;
10
10
  }
11
- declare const VercelProjectReferenceSchema: z.ZodObject<{
12
- projectId: z.ZodString;
13
- orgId: z.ZodString;
14
- projectName: z.ZodOptional<z.ZodString>;
15
- }, z.core.$strip>;
16
11
  /** Validated Vercel owner and project identifiers. */
17
- export type VercelProjectReference = z.infer<typeof VercelProjectReferenceSchema>;
12
+ export type VercelProjectReference = VercelProjectLink;
18
13
  /** Parses the complete Vercel owner and project environment pair. */
19
14
  export declare function projectReferenceFromEnvironment(environment: Readonly<Record<string, string | undefined>>): VercelProjectReference | undefined;
20
15
  /** Rejects Vercel's unsupported legacy link directory before link mutation. */
@@ -70,4 +65,3 @@ export declare function projectResolutionFromDeployResult(project: ProjectResolu
70
65
  }): ProjectResolution;
71
66
  export declare function isProjectResolved(project: ProjectResolution): boolean;
72
67
  export declare function projectProductionUrlFromResolution(project: ProjectResolution): string | undefined;
73
- export {};
@@ -1 +1 @@
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";import{captureVercel}from"./primitives/run-vercel.js";import{join}from"node:path";import{readFile,stat}from"node:fs/promises";const VercelProjectReferenceSchema=object({projectId:string().min(1),orgId:string().min(1),projectName:string().min(1).optional()}),VercelProjectEnvironmentSchema=object({VERCEL_ORG_ID:VercelProjectReferenceSchema.shape.orgId,VERCEL_PROJECT_ID:VercelProjectReferenceSchema.shape.projectId});function projectReferenceFromEnvironment(e){let t=VercelProjectEnvironmentSchema.safeParse(e);if(t.success)return{orgId:t.data.VERCEL_ORG_ID,projectId:t.data.VERCEL_PROJECT_ID}}async function assertNoLegacyProjectLinkDirectory(e){try{if(!(await stat(join(e,`.now`))).isDirectory())return}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}throw Error("Legacy Vercel link directory `.now` is not supported. Remove `.now` before linking this project.")}async function readProjectLink(e){await assertNoLegacyProjectLinkDirectory(e);try{let t=await readFile(join(e,`.vercel`,`project.json`),`utf8`),n=VercelProjectReferenceSchema.safeParse(JSON.parse(t));return n.success?n.data:void 0}catch{return}}function pickShortestAlias(e){if(!Array.isArray(e))return;let t;for(let n of e)typeof n!=`string`||n.length===0||(t===void 0||n.length<t.length)&&(t=n);return t}async function fetchProductionAlias(e,t,r,i){let a=await captureVercel([`api`,`/v9/projects/${e}?teamId=${t}`,`--scope`,t],{cwd:r,signal:i.signal});if(a.ok)try{let e=pickShortestAlias(JSON.parse(a.stdout).targets?.production?.alias);return e?`https://${e}`:void 0}catch{return}}async function detectDeployment(e,t={}){t.signal?.throwIfAborted();let n=await readProjectLink(e);if(n===void 0)return{state:`unlinked`};let r=await fetchProductionAlias(n.projectId,n.orgId,e,t);return t.signal?.throwIfAborted(),{state:r?`deployed`:`linked`,projectId:n.projectId,orgId:n.orgId,productionUrl:r}}async function fetchVercelName(e,t,r,i){let a=await captureVercel([`api`,e,`--scope`,t],{cwd:r,signal:i.signal});if(a.ok)try{let e=JSON.parse(a.stdout);return typeof e.name==`string`&&e.name.length>0?e.name:typeof e.slug==`string`&&e.slug.length>0?e.slug:void 0}catch{return}}async function detectProjectIdentity(e,t={}){t.signal?.throwIfAborted();let n=await readProjectLink(e);if(n===void 0)return;let[r,i]=await Promise.all([fetchVercelName(`/v9/projects/${n.projectId}?teamId=${n.orgId}`,n.orgId,e,t).then(e=>e??n.projectId),n.orgId.startsWith(`team_`)?fetchVercelName(`/v2/teams/${n.orgId}`,n.orgId,e,t):Promise.resolve(void 0)]);return t.signal?.throwIfAborted(),{projectName:r,teamName:i}}function projectResolutionFromDeployment(e){return e.state===`unlinked`||e.projectId===void 0?{kind:`unresolved`}:e.state===`deployed`&&e.productionUrl!==void 0?{kind:`deployed`,projectId:e.projectId,productionUrl:e.productionUrl}:{kind:`linked`,projectId:e.projectId}}async function detectProjectResolution(e,t={}){return projectResolutionFromDeployment(await detectDeployment(e,t))}function mergeProjectResolution(e,t){return t.kind===`unresolved`||e.kind===`deployed`&&e.projectId===t.projectId?e:t}function projectResolutionFromDeployResult(e,t){return e.kind===`unresolved`||!t.deployed||t.productionUrl===void 0?e:{kind:`deployed`,projectId:e.projectId,productionUrl:t.productionUrl}}function isProjectResolved(e){return e.kind!==`unresolved`}function projectProductionUrlFromResolution(e){return e.kind===`deployed`?e.productionUrl:void 0}export{assertNoLegacyProjectLinkDirectory,detectDeployment,detectProjectIdentity,detectProjectResolution,isProjectResolved,mergeProjectResolution,projectProductionUrlFromResolution,projectReferenceFromEnvironment,projectResolutionFromDeployResult,projectResolutionFromDeployment,readProjectLink};
1
+ import{object}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}from"./primitives/run-vercel.js";import{join}from"node:path";import{stat}from"node:fs/promises";import{VercelProjectLinkSchema,readVercelProjectLink}from"#internal/vercel/project-link.js";const VercelProjectEnvironmentSchema=object({VERCEL_ORG_ID:VercelProjectLinkSchema.shape.orgId,VERCEL_PROJECT_ID:VercelProjectLinkSchema.shape.projectId});function projectReferenceFromEnvironment(e){let t=VercelProjectEnvironmentSchema.safeParse(e);if(t.success)return{orgId:t.data.VERCEL_ORG_ID,projectId:t.data.VERCEL_PROJECT_ID}}async function assertNoLegacyProjectLinkDirectory(e){try{if(!(await stat(join(e,`.now`))).isDirectory())return}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}throw Error("Legacy Vercel link directory `.now` is not supported. Remove `.now` before linking this project.")}async function readProjectLink(e){return await assertNoLegacyProjectLinkDirectory(e),await readVercelProjectLink(e)}function pickShortestAlias(e){if(!Array.isArray(e))return;let t;for(let n of e)typeof n!=`string`||n.length===0||(t===void 0||n.length<t.length)&&(t=n);return t}async function fetchProductionAlias(e,n,r,i){let a=await captureVercel([`api`,`/v9/projects/${e}?teamId=${n}`,`--scope`,n],{cwd:r,signal:i.signal});if(a.ok)try{let e=pickShortestAlias(JSON.parse(a.stdout).targets?.production?.alias);return e?`https://${e}`:void 0}catch{return}}async function detectDeployment(e,t={}){t.signal?.throwIfAborted();let n=await readProjectLink(e);if(n===void 0)return{state:`unlinked`};let r=await fetchProductionAlias(n.projectId,n.orgId,e,t);return t.signal?.throwIfAborted(),{state:r?`deployed`:`linked`,projectId:n.projectId,orgId:n.orgId,productionUrl:r}}async function fetchVercelName(e,n,r,i){let a=await captureVercel([`api`,e,`--scope`,n],{cwd:r,signal:i.signal});if(a.ok)try{let e=JSON.parse(a.stdout);return typeof e.name==`string`&&e.name.length>0?e.name:typeof e.slug==`string`&&e.slug.length>0?e.slug:void 0}catch{return}}async function detectProjectIdentity(e,t={}){t.signal?.throwIfAborted();let n=await readProjectLink(e);if(n===void 0)return;let[r,i]=await Promise.all([fetchVercelName(`/v9/projects/${n.projectId}?teamId=${n.orgId}`,n.orgId,e,t).then(e=>e??n.projectId),n.orgId.startsWith(`team_`)?fetchVercelName(`/v2/teams/${n.orgId}`,n.orgId,e,t):Promise.resolve(void 0)]);return t.signal?.throwIfAborted(),{projectName:r,teamName:i}}function projectResolutionFromDeployment(e){return e.state===`unlinked`||e.projectId===void 0?{kind:`unresolved`}:e.state===`deployed`&&e.productionUrl!==void 0?{kind:`deployed`,projectId:e.projectId,productionUrl:e.productionUrl}:{kind:`linked`,projectId:e.projectId}}async function detectProjectResolution(e,t={}){return projectResolutionFromDeployment(await detectDeployment(e,t))}function mergeProjectResolution(e,t){return t.kind===`unresolved`||e.kind===`deployed`&&e.projectId===t.projectId?e:t}function projectResolutionFromDeployResult(e,t){return e.kind===`unresolved`||!t.deployed||t.productionUrl===void 0?e:{kind:`deployed`,projectId:e.projectId,productionUrl:t.productionUrl}}function isProjectResolved(e){return e.kind!==`unresolved`}function projectProductionUrlFromResolution(e){return e.kind===`deployed`?e.productionUrl:void 0}export{assertNoLegacyProjectLinkDirectory,detectDeployment,detectProjectIdentity,detectProjectResolution,isProjectResolved,mergeProjectResolution,projectProductionUrlFromResolution,projectReferenceFromEnvironment,projectResolutionFromDeployResult,projectResolutionFromDeployment,readProjectLink};
@@ -9,6 +9,8 @@ export type PrompterValue = string | number | boolean;
9
9
  export interface SelectOption<T extends PrompterValue> {
10
10
  value: T;
11
11
  label: string;
12
+ /** Completion action kept after searchable results instead of being filtered. */
13
+ trailingAction?: boolean;
12
14
  hint?: string;
13
15
  /** Short inline annotation shown dimmed only while the cursor is on this row. */
14
16
  focusHint?: string;
@@ -32,8 +32,9 @@ export type ConnectionAuthSpec =
32
32
  * It starts as a placeholder and is rewritten to the real connector UID once
33
33
  * the connector is provisioned (see {@link service}). `service` is the
34
34
  * managed-connector identifier passed to `vercel connect create <service>`
35
- * (e.g. the MCP host `mcp.linear.app`); when omitted, the connector must be
36
- * created out of band and its UID set by hand.
35
+ * (e.g. `mcp.linear.app`); when omitted, eve falls back to the MCP endpoint
36
+ * host. It is distinct from the connector UID, which Vercel assigns when the
37
+ * connector is created or listed.
37
38
  */
38
39
  {
39
40
  kind: "connect";
@@ -107,11 +108,15 @@ export declare function effectiveProtocols(declared: readonly ConnectionProtocol
107
108
  export declare function isValidConnectionSlug(slug: string): boolean;
108
109
  /**
109
110
  * The `vercel connect create <service>` identifier for a Connect-backed
110
- * connection: the explicit `auth.service` when set, otherwise the host of the
111
- * MCP endpoint (e.g. `mcp.linear.app`). Returns `undefined` when neither is
112
- * available, in which case the connector must be provisioned out of band.
111
+ * connection: the explicit `auth.service` when set, otherwise the MCP host.
112
+ * Returns `undefined` when neither is available, in which case the connector
113
+ * must be provisioned out of band.
113
114
  */
114
115
  export declare function connectorServiceForEntry(entry: Pick<ConnectionCatalogEntry, "mcp" | "auth">): string | undefined;
116
+ /** Canonical connector name attempted before offering discovery or creation. */
117
+ export declare function canonicalConnectorNameForEntry(entry: {
118
+ auth?: ConnectionAuthSpec;
119
+ }): string | undefined;
115
120
  /** Extracts the bare host from an MCP URL, or `undefined` when unparseable. */
116
121
  export declare function mcpServiceHost(url: string | undefined): string | undefined;
117
122
  /** Returns the endpoint block required for a protocol, or `null` when missing. */
@@ -1 +1 @@
1
- import{connectionEntries,connectionProtocols}from"../../../packages/eve-catalog/src/index.js";const SUPPORTED_PROTOCOLS=[`mcp`],CUSTOM_CONNECTION_SLUG=`custom`,CONNECTION_AUTH={linear:{kind:`connect`,connector:`linear`,service:`mcp.linear.app`},notion:{kind:`connect`,connector:`notion`,service:`mcp.notion.com`},datadog:{kind:`connect`,connector:`datadog`,service:`mcp.datadoghq.com`},honeycomb:{kind:`connect`,connector:`honeycomb`,service:`mcp.honeycomb.io`}};function buildCatalogEntry(e,n,r){let i=CONNECTION_AUTH[e];if(i===void 0)throw Error(`Connection "${e}" is in the catalog but has no scaffolder auth overlay.`);let a={slug:e,label:n,protocols:connectionProtocols(r),description:r.description,auth:i};return r.mcp&&(a.mcp={url:r.mcp.url}),r.openapi&&(a.openapi={spec:r.openapi.spec,baseUrl:r.openapi.baseUrl}),a}const CONNECTION_CATALOG=connectionEntries().map(e=>{if(e.connection===void 0)throw Error(`Catalog connection "${e.slug}" is missing its connection identity.`);return buildCatalogEntry(e.slug,e.name,e.connection)}),CATALOG_BY_SLUG=new Map(CONNECTION_CATALOG.map(e=>[e.slug,e]));function getCatalogEntry(e){return CATALOG_BY_SLUG.get(e)}function catalogSlugs(){return CONNECTION_CATALOG.map(e=>e.slug)}function effectiveProtocols(e){let t=e===void 0||e.length===0?SUPPORTED_PROTOCOLS:e;return SUPPORTED_PROTOCOLS.filter(e=>t.includes(e))}const CONNECTION_SLUG_PATTERN=/^[a-z][a-z0-9-]{0,63}$/;function isValidConnectionSlug(e){return CONNECTION_SLUG_PATTERN.test(e)}function connectorServiceForEntry(e){if(e.auth.kind===`connect`)return e.auth.service?e.auth.service:mcpServiceHost(e.mcp?.url)}function mcpServiceHost(e){if(e)try{return new URL(e).host||void 0}catch{return}}function endpointForProtocol(e,t){return t===`mcp`?e.mcp??null:e.openapi??null}export{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,catalogSlugs,connectorServiceForEntry,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug,mcpServiceHost};
1
+ import{connectionEntries,connectionProtocols}from"../../../packages/eve-catalog/src/index.js";const SUPPORTED_PROTOCOLS=[`mcp`],CUSTOM_CONNECTION_SLUG=`custom`,CONNECTION_AUTH={linear:{kind:`connect`,connector:`linear`,service:`mcp.linear.app`},notion:{kind:`connect`,connector:`notion`,service:`mcp.notion.com`},datadog:{kind:`connect`,connector:`datadog`,service:`mcp.datadoghq.com`},honeycomb:{kind:`connect`,connector:`honeycomb`,service:`mcp.honeycomb.io`}};function buildCatalogEntry(e,n,r){let i=CONNECTION_AUTH[e];if(i===void 0)throw Error(`Connection "${e}" is in the catalog but has no scaffolder auth overlay.`);let a={slug:e,label:n,protocols:connectionProtocols(r),description:r.description,auth:i};return r.mcp&&(a.mcp={url:r.mcp.url}),r.openapi&&(a.openapi={spec:r.openapi.spec,baseUrl:r.openapi.baseUrl}),a}const CONNECTION_CATALOG=connectionEntries().map(e=>{if(e.connection===void 0)throw Error(`Catalog connection "${e.slug}" is missing its connection identity.`);return buildCatalogEntry(e.slug,e.name,e.connection)}),CATALOG_BY_SLUG=new Map(CONNECTION_CATALOG.map(e=>[e.slug,e]));function getCatalogEntry(e){return CATALOG_BY_SLUG.get(e)}function catalogSlugs(){return CONNECTION_CATALOG.map(e=>e.slug)}function effectiveProtocols(e){let t=e===void 0||e.length===0?SUPPORTED_PROTOCOLS:e;return SUPPORTED_PROTOCOLS.filter(e=>t.includes(e))}const CONNECTION_SLUG_PATTERN=/^[a-z][a-z0-9-]{0,63}$/;function isValidConnectionSlug(e){return CONNECTION_SLUG_PATTERN.test(e)}function connectorServiceForEntry(e){if(e.auth.kind===`connect`)return e.auth.service?e.auth.service:mcpServiceHost(e.mcp?.url)}function canonicalConnectorNameForEntry(e){if(e.auth?.kind!==`connect`)return;let t=e.auth.connector.trim();return t.length>0?t:void 0}function mcpServiceHost(e){if(e)try{return new URL(e).host||void 0}catch{return}}function endpointForProtocol(e,t){return t===`mcp`?e.mcp??null:e.openapi??null}export{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,canonicalConnectorNameForEntry,catalogSlugs,connectorServiceForEntry,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug,mcpServiceHost};