eve 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/src/cli/commands/agent-instructions.d.ts +15 -3
  3. package/dist/src/cli/commands/agent-instructions.js +3 -1
  4. package/dist/src/cli/commands/agent-prompt/build-and-verify.md +33 -0
  5. package/dist/src/cli/commands/agent-prompt/collect-intent.md +18 -0
  6. package/dist/src/cli/commands/agent-prompt/intro-handoff.md +4 -0
  7. package/dist/src/cli/commands/agent-prompt/intro-setup.md +5 -0
  8. package/dist/src/cli/commands/agent-prompt/scaffold.md +19 -0
  9. package/dist/src/cli/commands/agent-prompt/vercel-connect.md +3 -0
  10. package/dist/src/cli/commands/deploy.js +1 -1
  11. package/dist/src/cli/commands/init-repl.d.ts +79 -0
  12. package/dist/src/cli/commands/init-repl.js +1 -0
  13. package/dist/src/cli/commands/init.d.ts +6 -0
  14. package/dist/src/cli/commands/init.js +1 -1
  15. package/dist/src/cli/commands/link.d.ts +2 -2
  16. package/dist/src/cli/commands/link.js +1 -1
  17. package/dist/src/cli/dev/tui/line-editor.d.ts +2 -0
  18. package/dist/src/cli/dev/tui/line-editor.js +2 -2
  19. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  20. package/dist/src/cli/dev/tui/setup-commands.d.ts +26 -7
  21. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  22. package/dist/src/cli/dev/tui/setup-flow.d.ts +10 -3
  23. package/dist/src/cli/dev/tui/setup-panel.d.ts +16 -8
  24. package/dist/src/cli/dev/tui/setup-panel.js +3 -3
  25. package/dist/src/cli/dev/tui/setup-selection-input.d.ts +2 -1
  26. package/dist/src/cli/dev/tui/setup-selection-input.js +2 -2
  27. package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
  28. package/dist/src/cli/dev/tui/terminal-text.d.ts +11 -0
  29. package/dist/src/cli/dev/tui/terminal-text.js +1 -1
  30. package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
  31. package/dist/src/cli/run.d.ts +1 -0
  32. package/dist/src/cli/run.js +2 -2
  33. package/dist/src/cli/ui/live-row.js +1 -1
  34. package/dist/src/cli/ui/progress-pulse.d.ts +10 -0
  35. package/dist/src/cli/ui/progress-pulse.js +1 -0
  36. package/dist/src/discover/discover-agent.js +1 -1
  37. package/dist/src/evals/target.js +1 -1
  38. package/dist/src/execution/node-step.js +1 -1
  39. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  40. package/dist/src/execution/sandbox/bindings/vercel-sdk-types.d.ts +0 -1
  41. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  42. package/dist/src/execution/sandbox/multiplexed-command.d.ts +22 -0
  43. package/dist/src/execution/sandbox/multiplexed-command.js +1 -0
  44. package/dist/src/execution/tool-auth.d.ts +17 -30
  45. package/dist/src/execution/tool-auth.js +1 -1
  46. package/dist/src/execution/workflow-steps.js +1 -1
  47. package/dist/src/harness/tool-loop.js +1 -1
  48. package/dist/src/internal/application/package.js +1 -1
  49. package/dist/src/internal/authored-module-loader.js +2 -2
  50. package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
  51. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
  52. package/dist/src/public/channels/github/api.d.ts +5 -0
  53. package/dist/src/public/channels/github/api.js +1 -1
  54. package/dist/src/public/channels/github/dispatch.js +1 -1
  55. package/dist/src/public/channels/github/pr-context.d.ts +10 -0
  56. package/dist/src/public/channels/github/pr-context.js +2 -2
  57. package/dist/src/public/channels/slack/connections.d.ts +16 -18
  58. package/dist/src/public/channels/slack/defaults.d.ts +1 -1
  59. package/dist/src/public/channels/slack/defaults.js +1 -1
  60. package/dist/src/public/channels/slack/slackChannel.d.ts +6 -7
  61. package/dist/src/public/definitions/tool.d.ts +40 -41
  62. package/dist/src/public/definitions/tool.js +1 -1
  63. package/dist/src/public/tools/index.d.ts +1 -1
  64. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  65. package/dist/src/runtime/connections/validate-authorization.js +1 -1
  66. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  67. package/dist/src/runtime/resolve-tool.js +1 -1
  68. package/dist/src/runtime/types.d.ts +0 -10
  69. package/dist/src/setup/ai-gateway-api-key.d.ts +15 -0
  70. package/dist/src/setup/ai-gateway-api-key.js +1 -0
  71. package/dist/src/setup/boxes/apply-ai-gateway-credential.js +1 -1
  72. package/dist/src/setup/boxes/detect-ai-gateway.js +2 -2
  73. package/dist/src/setup/boxes/link-project.d.ts +8 -14
  74. package/dist/src/setup/boxes/link-project.js +1 -1
  75. package/dist/src/setup/boxes/resolve-provisioning.d.ts +2 -1
  76. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  77. package/dist/src/setup/cli/select-component.d.ts +7 -1
  78. package/dist/src/setup/cli/select-component.js +1 -1
  79. package/dist/src/setup/cli/select-option-codec.d.ts +1 -0
  80. package/dist/src/setup/cli/select-option-codec.js +1 -1
  81. package/dist/src/setup/cli/select-state.d.ts +14 -1
  82. package/dist/src/setup/cli/select-state.js +1 -1
  83. package/dist/src/setup/flows/channels.js +1 -1
  84. package/dist/src/setup/flows/deploy.js +1 -1
  85. package/dist/src/setup/flows/install-vercel-cli.js +1 -1
  86. package/dist/src/setup/flows/link.d.ts +2 -1
  87. package/dist/src/setup/flows/link.js +1 -1
  88. package/dist/src/setup/flows/login.js +1 -1
  89. package/dist/src/setup/flows/model.d.ts +5 -4
  90. package/dist/src/setup/flows/model.js +1 -1
  91. package/dist/src/setup/flows/vercel.js +2 -2
  92. package/dist/src/setup/index.d.ts +1 -1
  93. package/dist/src/setup/package-manager.d.ts +2 -1
  94. package/dist/src/setup/package-manager.js +1 -1
  95. package/dist/src/setup/primitives/pm/index.d.ts +1 -1
  96. package/dist/src/setup/primitives/pm/pnpm.d.ts +11 -1
  97. package/dist/src/setup/primitives/pm/pnpm.js +5 -2
  98. package/dist/src/setup/primitives/pm/types.d.ts +10 -1
  99. package/dist/src/setup/project-resolution.d.ts +9 -6
  100. package/dist/src/setup/project-resolution.js +1 -1
  101. package/dist/src/setup/prompter.d.ts +13 -0
  102. package/dist/src/setup/prompter.js +1 -1
  103. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  104. package/dist/src/setup/scaffold/create/project.d.ts +8 -0
  105. package/dist/src/setup/scaffold/create/project.js +8 -7
  106. package/dist/src/setup/scaffold/update/channels.d.ts +8 -0
  107. package/dist/src/setup/scaffold/update/channels.js +2 -2
  108. package/dist/src/setup/scaffold/update/package-json.d.ts +3 -0
  109. package/dist/src/setup/scaffold/update/package-json.js +1 -1
  110. package/dist/src/setup/scaffold/workspace-glob.d.ts +5 -0
  111. package/dist/src/setup/scaffold/workspace-glob.js +1 -0
  112. package/dist/src/setup/scaffold/workspace-root.d.ts +29 -0
  113. package/dist/src/setup/scaffold/workspace-root.js +1 -0
  114. package/dist/src/setup/state.d.ts +7 -2
  115. package/dist/src/setup/vercel-api-failure.d.ts +9 -0
  116. package/dist/src/setup/vercel-api-failure.js +1 -0
  117. package/dist/src/setup/vercel-deployment.js +1 -1
  118. package/dist/src/setup/vercel-project-api.d.ts +25 -0
  119. package/dist/src/setup/vercel-project-api.js +1 -0
  120. package/dist/src/setup/vercel-project.d.ts +11 -57
  121. package/dist/src/setup/vercel-project.js +1 -1
  122. package/dist/src/setup/with-spinner.d.ts +3 -0
  123. package/dist/src/setup/with-spinner.js +1 -0
  124. package/dist/src/shared/package-name.d.ts +4 -0
  125. package/dist/src/shared/package-name.js +1 -0
  126. package/docs/channels/slack.mdx +1 -1
  127. package/docs/connections.mdx +9 -6
  128. package/docs/getting-started.mdx +8 -3
  129. package/docs/guides/auth-and-route-protection.md +57 -10
  130. package/docs/guides/dev-tui.md +3 -3
  131. package/docs/reference/cli.md +3 -3
  132. package/docs/reference/typescript-api.md +7 -7
  133. package/package.json +1 -1
  134. package/dist/src/cli/commands/init-agent-handoff.md +0 -12
  135. package/dist/src/cli/commands/init-agent-instructions.md +0 -16
@@ -1,9 +1,9 @@
1
- import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.12.2`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.13.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
2
2
 
3
3
  export default defineAgent({
4
4
  model: "__EVE_INIT_MODEL__",
5
5
  });
6
- `;function packageJsonTemplate(){return`{
6
+ `;function packageJsonTemplate(e){return`{
7
7
  "name": "__EVE_INIT_APP_NAME__",
8
8
  "version": "0.0.0",
9
9
  "type": "module",
@@ -26,7 +26,9 @@ export default defineAgent({
26
26
  "devDependencies": {
27
27
  "@types/node": "__EVE_INIT_TYPES_NODE_VERSION__",
28
28
  "typescript": "__EVE_INIT_TYPESCRIPT_VERSION__"
29
- },
29
+ }${e?ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX:``}
30
+ }
31
+ `}const ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX=`,
30
32
  "overrides": {
31
33
  "ai": "__EVE_INIT_AI_SDK_VERSION__"
32
34
  },
@@ -36,8 +38,7 @@ export default defineAgent({
36
38
  "engines": {
37
39
  "node": "__EVE_INIT_NODE_ENGINE__"
38
40
  }
39
- }
40
- `}const AGENT_INSTRUCTIONS_TEMPLATE=`# Identity
41
+ `,AGENT_INSTRUCTIONS_TEMPLATE=`# Identity
41
42
 
42
43
  You are a helpful assistant.
43
44
  `,SHARED_TEMPLATE_FILES={"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE,"tsconfig.json":`{
@@ -76,7 +77,7 @@ dist
76
77
 
77
78
  This project uses the eve framework. Before writing code, always read the relevant guide in \`node_modules/eve/docs/\`.
78
79
  `,"CLAUDE.md":`@AGENTS.md
79
- `};function templateFiles(t,n){return{"agent/agent.ts":t?`import { defineAgent } from "eve";
80
+ `};function templateFiles(e,t){return{"agent/agent.ts":e?`import { defineAgent } from "eve";
80
81
 
81
82
  export default defineAgent({
82
83
  model: "__EVE_INIT_MODEL__",
@@ -90,4 +91,4 @@ export default defineAgent({
90
91
  },
91
92
  },
92
93
  });
93
- `:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(),...getPackageManagerStrategy(n).scaffoldFiles}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let r=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let a=resolve(e.targetDirectory??process.cwd(),e.projectName),o=e.projectName===`.`,c=e.overwriteExisting??!1,u=e.byokProvider??!1,d=e.packageManager??`pnpm`,f=resolveEvePackageContract(e.evePackage),p=pinnedNodeEngineMajor(f.nodeEngine);if(o)await assertCanCreateInPlace(a,c);else if(await pathExists(a))throw Error(`Cannot create project because "${a}" already exists.`);let m={appName:basename(a),model:e.model,eveVersion:f.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`7.0.0-beta.178`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.2.2`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.4.3`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,e.typescriptPackageVersion??`7.0.1-rc`),nodeTypesVersion:p,nodeEngine:p};await mkdir(a,{recursive:!0});for(let[t,i]of Object.entries(templateFiles(u,d))){let s=`${a}/${t}`,l=await pathExists(s);await writeTextFile(s,renderTemplate(i,m),{force:o&&c}),l&&await e.onOverwriteFile?.(s)}return a}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
94
+ `:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(t)}}async function assertCanCreateInPlace(e,n){if(!await pathExists(e))return;let r=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(r.length>0&&!n){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(o){let s=resolve(o.targetDirectory??process.cwd(),o.projectName),c=o.projectName===`.`,l=o.overwriteExisting??!1,u=o.byokProvider??!1,d=o.packageManager??`pnpm`,f=resolveEvePackageContract(o.evePackage),p=pinnedNodeEngineMajor(f.nodeEngine),m=resolve(o.workspaceProbeDirectory??s),h=isPackageManagerWorkspaceMember(d,m);if(c)await assertCanCreateInPlace(s,l);else if(await pathExists(s))throw Error(`Cannot create project because "${s}" already exists.`);let g={appName:basename(s),model:o.model,eveVersion:f.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,o.aiPackageVersion??`7.0.0-beta.178`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,o.connectPackageVersion??`0.2.2`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,o.zodPackageVersion??`4.4.3`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,o.typescriptPackageVersion??`7.0.1-rc`),nodeTypesVersion:p,nodeEngine:p};await mkdir(s,{recursive:!0});for(let[e,r]of Object.entries(templateFiles(u,!h))){let i=`${s}/${e}`,a=await pathExists(i);await writeTextFile(i,renderTemplate(r,g),{force:c&&l}),a&&await o.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:d,projectRoot:s,workspaceProbeRoot:m,onWorkspaceRootMutation:o.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(d,m,{aiPackageVersion:g.aiPackageVersion,nodeEngineRequirement:f.nodeEngine,onWorkspaceRootMutation:o.onWorkspaceRootMutation}),s}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
@@ -1,5 +1,6 @@
1
1
  import type { PackageManagerKind } from "../../package-manager.js";
2
2
  import { type NodeEngineOverride } from "../../node-engine.js";
3
+ import { type WorkspaceRootMutation } from "../workspace-root.js";
3
4
  import { type EvePackageContract } from "../create/project.js";
4
5
  export declare const SLACK_CHANNEL_DEFAULT_ROUTE = "/eve/v1/slack";
5
6
  export declare const DEFAULT_SLACK_CONNECTOR_SLUG = "my-agent";
@@ -76,6 +77,12 @@ export interface EnsureChannelOptions {
76
77
  kind: ChannelKind;
77
78
  /** Manager that owns generated project configuration. Defaults to pnpm. */
78
79
  packageManager?: PackageManagerKind;
80
+ /**
81
+ * Final project path used to discover ancestor workspaces. This differs from
82
+ * `projectRoot` only when scaffolding writes into a temporary staging
83
+ * directory before moving the project into place.
84
+ */
85
+ workspaceProbeDirectory?: string;
79
86
  force?: boolean;
80
87
  /** Exact UID returned by Vercel Connect; takes precedence over the derived slug. */
81
88
  slackConnectorUid?: string;
@@ -84,6 +91,7 @@ export interface EnsureChannelOptions {
84
91
  webPackageVersions?: WebPackageVersions;
85
92
  /** When false, Web Chat leaves Vercel Services config unwritten for preview-only scaffolds. */
86
93
  configureVercelServices?: boolean;
94
+ onWorkspaceRootMutation?: (mutation: WorkspaceRootMutation) => void | Promise<void>;
87
95
  }
88
96
  export interface WebPackageVersions {
89
97
  evePackage?: EvePackageContract;
@@ -1,7 +1,7 @@
1
- import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"../create/web-template.js";import{DEFAULT_EVE_PACKAGE_CONTRACT,resolveEvePackageContract}from"../create/project.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,VERCEL_HOST_FRAMEWORK_PACKAGE_NAMES=[`@sveltejs/kit`,NEXT_PACKAGE_NAME,`nuxt`,`nuxt3`,`nuxt-edge`,`nuxt-nightly`],PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH),WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},eve:{buildCommand:`eve build`,entrypoint:`.`,framework:`eve`,routePrefix:`/_eve_internal/eve`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function hasAnyPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)?t.some(e=>packageJsonHasDependency(r,e)):!1}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}async function hasVercelHostFramework(e){return hasAnyPackageDependency(join(e,`package.json`),VERCEL_HOST_FRAMEWORK_PACKAGE_NAMES)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`7.0.0-beta.178`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,r){if(!await pathExists(e))return{mutations:[]};let o=resolveEvePackageContract(r.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,r.aiPackageVersion),eve:formatEveDependencySpecifier(o.version),next:resolveVersionToken(`nextPackageVersion`,r.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,r.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,r.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,r.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,r.zodPackageVersion)},u={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":s,"@types/react":resolveVersionToken(`typesReactPackageVersion`,r.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,r.typesReactDomPackageVersion),typescript:`6.0.3`},f=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,p=await patchPackageJson(e,{dependencies:c,devDependencies:u,scripts:f,nodeEngineRequirement:o.nodeEngine});return{mutations:[{path:e,dependencies:Object.keys(c),devDependencies:Object.keys(u),scripts:Object.keys(f)}],nodeEngineOverride:p.nodeEngineOverride}}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"../create/web-template.js";import{DEFAULT_EVE_PACKAGE_CONTRACT,resolveEvePackageContract}from"../create/project.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,VERCEL_HOST_FRAMEWORK_PACKAGE_NAMES=[`@sveltejs/kit`,NEXT_PACKAGE_NAME,`nuxt`,`nuxt3`,`nuxt-edge`,`nuxt-nightly`],PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH),WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},eve:{buildCommand:`eve build`,entrypoint:`.`,framework:`eve`,routePrefix:`/_eve_internal/eve`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,n){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,n)}async function hasAnyPackageDependency(e,n){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)?n.some(e=>packageJsonHasDependency(r,e)):!1}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}async function hasVercelHostFramework(e){return hasAnyPackageDependency(join(e,`package.json`),VERCEL_HOST_FRAMEWORK_PACKAGE_NAMES)}async function ensurePackageDependency(e,n,i){return!await pathExists(e)||await readDependencyVersion(e,n)===i?[]:(await patchPackageJson(e,{dependencies:{[n]:i}}),[{path:e,dependencies:[n],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`7.0.0-beta.178`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(n,a,c,l,u){let f=join(n,`package.json`);if(!await pathExists(f))return{mutations:[]};let m=resolveEvePackageContract(l.evePackage),h=pinnedNodeEngineMajor(m.nodeEngine),g={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,l.aiPackageVersion),eve:formatEveDependencySpecifier(m.version),next:resolveVersionToken(`nextPackageVersion`,l.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,l.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,l.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,l.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,l.zodPackageVersion)},_={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":h,"@types/react":resolveVersionToken(`typesReactPackageVersion`,l.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,l.typesReactDomPackageVersion),typescript:`6.0.3`},v=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,y=isPackageManagerWorkspaceMember(a,c),b={dependencies:g,devDependencies:_,scripts:v};y||(b.nodeEngineRequirement=m.nodeEngine);let x=await patchPackageJson(f,b),S=(await patchWorkspaceRootPackageJson(a,c,{aiPackageVersion:g.ai,nodeEngineRequirement:m.nodeEngine,onWorkspaceRootMutation:u})).nodeEngineOverride??x.nodeEngineOverride;return{mutations:[{path:f,dependencies:Object.keys(g),devDependencies:Object.keys(_),scripts:Object.keys(v)}],nodeEngineOverride:S}}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
2
2
  import { slackChannel } from "eve/channels/slack";
3
3
 
4
4
  export default slackChannel({
5
5
  credentials: connectSlackCredentials(${JSON.stringify(e)}),
6
6
  });
7
- `}function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,eve:n?.eve??WEB_DEFAULT_VERCEL_SERVICES.eve}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let r of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,r);await pathExists(i)&&t.push(i)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(t){let i=join(t.projectRoot,`package.json`),a=await pathExists(join(t.projectRoot,`app/page.tsx`));if(!t.force&&await isNextJsProject(t.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[i],packageJsonUpdated:[]};let o=await patchWebPackageJson(i,resolveWebPackageVersions(t.webPackageVersions)),s=[],l=[],u=[],d=[],m=basename(resolve(t.projectRoot));if(t.configureVercelServices??!0){let e=join(t.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(e)===`written`?s.push(e):d.push(e)}let h=await getPackageManagerStrategy(t.packageManager??`pnpm`).applyProjectConfiguration(t.projectRoot);s.push(...h.filesWritten),d.push(...h.filesSkipped);for(let[e,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(t.projectRoot,e);if(e===`agent/channels/eve.ts`&&!t.force&&await pathExists(a)){d.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,m),{force:!0}),s.push(a),o&&l.push(a)}u.push(...await findCompetingNextConfigFiles(t.projectRoot));let g={kind:`web`,action:a?`overwritten`:`created`,filesWritten:s,filesSkipped:d,packageJsonUpdated:o.mutations};return l.length>0&&(g.filesOverwritten=l),u.length>0&&(g.competingNextConfigFiles=u),o.nodeEngineOverride!==void 0&&(g.nodeEngineOverride=o.nodeEngineOverride),g}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.2.2`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return i&&(l.filesOverwritten=[t]),l}async function listAuthoredChannels(e){let t=join(e,`agent/channels`),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug};
7
+ `}function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,eve:n?.eve??WEB_DEFAULT_VERCEL_SERVICES.eve}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let r=await readFile(e,`utf8`),i=withWebVercelServices(r);return i===r?`skipped`:(await writeFile(e,i,`utf8`),`written`)}async function findCompetingNextConfigFiles(e){let n=[];for(let r of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,r);await pathExists(i)&&n.push(i)}return n}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let r=join(e.projectRoot,`package.json`),i=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await isNextJsProject(e.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[r],packageJsonUpdated:[]};let o=resolveWebPackageVersions(e.webPackageVersions),s=e.packageManager??`pnpm`,c=resolve(e.workspaceProbeDirectory??e.projectRoot),l=await patchWebPackageJson(e.projectRoot,s,c,o,e.onWorkspaceRootMutation),d=[],f=[],p=[],g=[],_=basename(resolve(e.projectRoot));if(e.configureVercelServices??!0){let t=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(t)===`written`?d.push(t):g.push(t)}let v=await applyPackageManagerWorkspaceConfiguration({packageManager:s,projectRoot:e.projectRoot,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation});d.push(...v.filesWritten),g.push(...v.filesSkipped);for(let[r,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,r);if(r===`agent/channels/eve.ts`&&!e.force&&await pathExists(a)){g.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,_),{force:!0}),d.push(a),o&&f.push(a)}p.push(...await findCompetingNextConfigFiles(e.projectRoot));let y=[...new Set(d)],b=[...new Set(g)].filter(e=>!y.includes(e)),x={kind:`web`,action:i?`overwritten`:`created`,filesWritten:y,filesSkipped:b,packageJsonUpdated:l.mutations};return f.length>0&&(x.filesOverwritten=f),p.length>0&&(x.competingNextConfigFiles=p),l.nodeEngineOverride!==void 0&&(x.nodeEngineOverride=l.nodeEngineOverride),x}async function ensureSlackChannel(e){let r=join(e.projectRoot,`agent/channels/slack.ts`),a=await pathExists(r);if(!e.force&&a)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[r],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.2.2`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(r,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:a?`overwritten`:`created`,filesWritten:[r],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return a&&(l.filesOverwritten=[r]),l}async function listAuthoredChannels(e){let t=join(e,`agent/channels`),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug};
@@ -2,6 +2,8 @@ import { type NodeEngineOverride } from "../../node-engine.js";
2
2
  export interface PackageJsonPatch {
3
3
  dependencies?: Record<string, string>;
4
4
  devDependencies?: Record<string, string>;
5
+ overrides?: Record<string, string>;
6
+ resolutions?: Record<string, string>;
5
7
  scripts?: Record<string, string>;
6
8
  /**
7
9
  * eve's required Node.js range (e.g. `">=24"`). When the target's
@@ -12,6 +14,7 @@ export interface PackageJsonPatch {
12
14
  nodeEngineRequirement?: string;
13
15
  }
14
16
  export interface PackageJsonPatchResult {
17
+ changed: boolean;
15
18
  nodeEngineOverride?: NodeEngineOverride;
16
19
  }
17
20
  export declare function patchPackageJson(path: string, patch: PackageJsonPatch): Promise<PackageJsonPatchResult>;
@@ -1 +1 @@
1
- import{reconcileNodeEngine}from"../../node-engine.js";import{readFile,writeFile}from"node:fs/promises";function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function patchPackageJson(r,i){let a=await readFile(r,`utf8`),o=JSON.parse(a),s=!1,c;if(i.dependencies!==void 0&&(o.dependencies={...o.dependencies,...i.dependencies},s=!0),i.devDependencies!==void 0&&(o.devDependencies={...o.devDependencies,...i.devDependencies},s=!0),i.scripts!==void 0&&(o.scripts={...o.scripts,...i.scripts},s=!0),i.nodeEngineRequirement!==void 0){let t=isJsonObject(o.engines)?o.engines:{},n=reconcileNodeEngine(t.node,i.nodeEngineRequirement);(n.kind===`added`||n.kind===`overridden`)&&(o.engines={...t,node:n.next},s=!0),n.kind===`overridden`&&(c=n)}return s&&await writeFile(r,`${JSON.stringify(o,null,2)}\n`,`utf8`),{nodeEngineOverride:c}}export{patchPackageJson};
1
+ import{reconcileNodeEngine}from"../../node-engine.js";import{readFile,writeFile}from"node:fs/promises";function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function patchPackageJson(r,i){let a=await readFile(r,`utf8`),o=JSON.parse(a),s=!1,c;if(i.dependencies!==void 0&&(o.dependencies={...o.dependencies,...i.dependencies},s=!0),i.devDependencies!==void 0&&(o.devDependencies={...o.devDependencies,...i.devDependencies},s=!0),i.overrides!==void 0&&(o.overrides={...isJsonObject(o.overrides)?o.overrides:{},...i.overrides},s=!0),i.resolutions!==void 0&&(o.resolutions={...isJsonObject(o.resolutions)?o.resolutions:{},...i.resolutions},s=!0),i.scripts!==void 0&&(o.scripts={...o.scripts,...i.scripts},s=!0),i.nodeEngineRequirement!==void 0){let t=isJsonObject(o.engines)?o.engines:{},n=reconcileNodeEngine(t.node,i.nodeEngineRequirement);(n.kind===`added`||n.kind===`overridden`)&&(o.engines={...t,node:n.next},s=!0),n.kind===`overridden`&&(c=n)}return s&&await writeFile(r,`${JSON.stringify(o,null,2)}\n`,`utf8`),{changed:s,nodeEngineOverride:c}}export{patchPackageJson};
@@ -0,0 +1,5 @@
1
+ export declare function workspaceRelativePath(workspaceRoot: string, projectRoot: string): string;
2
+ export declare function isPathInside(parentPath: string, childPath: string): boolean;
3
+ export declare function workspacePatternForProject(workspaceRoot: string, projectRoot: string): string;
4
+ export declare function workspacePatternClaimsRelativePath(pattern: string, relativePath: string): boolean;
5
+ export declare function workspacePatternsClaimProject(patterns: readonly string[], workspaceRoot: string, projectRoot: string): boolean;
@@ -0,0 +1 @@
1
+ import{isAbsolute,relative,resolve,sep}from"node:path";function escapeRegex(e){return e.replace(/[\\^$+?.()|[\]{}]/gu,`\\$&`)}function workspacePatternToRegex(e){let t=e.replaceAll(`\\`,`/`).replace(/^\.\/+/u,``),n=`^`;for(let e=0;e<t.length;e+=1){let r=t[e];r===`*`?t[e+1]===`*`?(n+=`.*`,e+=1):n+=`[^/]*`:n+=escapeRegex(r??``)}return n+=`$`,new RegExp(n,`u`)}function workspaceRelativePath(e,i){return relative(e,resolve(i)).split(sep).join(`/`)}function isPathInside(r,i){let a=relative(resolve(r),resolve(i));return a===``||!a.startsWith(`..`)&&!isAbsolute(a)}function workspacePatternForProject(e,t){let n=workspaceRelativePath(e,t);if(n.length===0)return`.`;let r=n.split(`/`);return r.length===1?n:`${r.slice(0,-1).join(`/`)}/*`}function workspacePatternClaimsRelativePath(e,t){let n=e.replaceAll(`\\`,`/`).replace(/^\.\/+/u,``);return n===`.`||n===t?!0:workspacePatternToRegex(n).test(t)}function workspacePatternsClaimProject(e,t,n){let r=workspaceRelativePath(t,n),i=e.filter(e=>!e.startsWith(`!`)),a=e.filter(e=>e.startsWith(`!`)).map(e=>e.slice(1));return i.some(e=>workspacePatternClaimsRelativePath(e,r))?!a.some(e=>workspacePatternClaimsRelativePath(e,r)):!1}export{isPathInside,workspacePatternClaimsRelativePath,workspacePatternForProject,workspacePatternsClaimProject,workspaceRelativePath};
@@ -0,0 +1,29 @@
1
+ import type { NodeEngineOverride } from "../node-engine.js";
2
+ import type { PackageManagerKind } from "../package-manager.js";
3
+ import type { PackageJsonPatchResult } from "./update/package-json.js";
4
+ export interface WorkspaceRootMutation {
5
+ kind: "package-json" | "workspace-config";
6
+ nodeEngineOverride?: NodeEngineOverride;
7
+ path: string;
8
+ }
9
+ export interface WorkspaceRootPackageJsonPatchResult extends PackageJsonPatchResult {
10
+ /** Root package.json path when an ancestor workspace root was eligible for patching. */
11
+ path?: string;
12
+ }
13
+ export declare function isPackageManagerWorkspaceMember(packageManager: PackageManagerKind, projectRoot: string): boolean;
14
+ /** Applies root-only package.json fields to an ancestor workspace package.json. */
15
+ export declare function patchWorkspaceRootPackageJson(packageManager: PackageManagerKind, projectRoot: string, input: {
16
+ readonly aiPackageVersion?: string;
17
+ readonly nodeEngineRequirement?: string;
18
+ readonly onWorkspaceRootMutation?: (mutation: WorkspaceRootMutation) => void | Promise<void>;
19
+ }): Promise<WorkspaceRootPackageJsonPatchResult>;
20
+ /** Applies manager-owned project files and reports ancestor workspace mutations. */
21
+ export declare function applyPackageManagerWorkspaceConfiguration(input: {
22
+ readonly packageManager: PackageManagerKind;
23
+ readonly projectRoot: string;
24
+ readonly workspaceProbeRoot?: string;
25
+ readonly onWorkspaceRootMutation?: (mutation: WorkspaceRootMutation) => void | Promise<void>;
26
+ }): Promise<{
27
+ filesSkipped: string[];
28
+ filesWritten: string[];
29
+ }>;
@@ -0,0 +1 @@
1
+ import{isPathInside,workspacePatternForProject,workspacePatternsClaimProject}from"./workspace-glob.js";import{ensurePnpmWorkspaceIncludesProject,findAncestorPnpmWorkspaceRoot}from"../primitives/pm/pnpm.js";import{getPackageManagerStrategy}from"../primitives/pm/index.js";import{patchPackageJson}from"./update/package-json.js";import{existsSync,readFileSync}from"node:fs";import{dirname,join,resolve}from"node:path";import{readFile,writeFile}from"node:fs/promises";function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function packageJsonWorkspacePatterns(e){let t=e.workspaces;return Array.isArray(t)?t.filter(e=>typeof e==`string`):!isJsonObject(t)||!Array.isArray(t.packages)?[]:t.packages.filter(e=>typeof e==`string`)}function readPackageJsonWorkspacePatterns(e){let t=JSON.parse(readFileSync(e,`utf8`));if(!isJsonObject(t))return;let n=packageJsonWorkspacePatterns(t);return n.length===0?void 0:n}function findAncestorPackageJsonWorkspaceRoot(e){let t=dirname(resolve(e));for(;;){let e=join(t,`package.json`);if(existsSync(e))try{if(readPackageJsonWorkspacePatterns(e)!==void 0)return t}catch{}let n=dirname(t);if(n===t)return;t=n}}function findClaimingPackageJsonWorkspaceRoot(e){let t=findAncestorPackageJsonWorkspaceRoot(e);if(t===void 0)return;let r;try{r=readPackageJsonWorkspacePatterns(join(t,`package.json`))}catch{return}return r!==void 0&&workspacePatternsClaimProject(r,t,e)?t:void 0}function findPackageManagerWorkspaceRoot(e,t){switch(e){case`pnpm`:return findAncestorPnpmWorkspaceRoot(t);case`bun`:case`npm`:case`yarn`:return findAncestorPackageJsonWorkspaceRoot(t)}}function isPackageManagerWorkspaceMember(e,t){let n=findPackageManagerWorkspaceRoot(e,t);return n!==void 0&&resolve(n)!==resolve(t)}function workspaceRootPackageJsonPath(e,t){let n=findPackageManagerWorkspaceRoot(e,t);return n===void 0?void 0:join(n,`package.json`)}async function ensurePackageJsonWorkspaceIncludesProject(e){let n=findAncestorPackageJsonWorkspaceRoot(e);if(n===void 0||resolve(n)===resolve(e)||findClaimingPackageJsonWorkspaceRoot(e)===n)return;let r=join(n,`package.json`),i=JSON.parse(await readFile(r,`utf8`)),a=workspacePatternForProject(n,e);if(Array.isArray(i.workspaces))i.workspaces=[...i.workspaces,a];else if(isJsonObject(i.workspaces)&&Array.isArray(i.workspaces.packages))i.workspaces={...i.workspaces,packages:[...i.workspaces.packages,a]};else return;return await writeFile(r,`${JSON.stringify(i,null,2)}\n`,`utf8`),r}async function ensurePackageManagerWorkspaceIncludesProject(e,t){switch(e){case`pnpm`:{let e=findAncestorPnpmWorkspaceRoot(t);return e===void 0?void 0:await ensurePnpmWorkspaceIncludesProject(t)===`written`?join(e,`pnpm-workspace.yaml`):void 0}case`bun`:case`npm`:case`yarn`:return ensurePackageJsonWorkspaceIncludesProject(t)}}function packageManagerRootOnlyPackageJsonPatch(e,t){let n={};if(t.nodeEngineRequirement!==void 0&&(n.nodeEngineRequirement=t.nodeEngineRequirement),t.aiPackageVersion!==void 0)switch(e){case`bun`:case`npm`:n.overrides={ai:t.aiPackageVersion};break;case`yarn`:n.resolutions={ai:t.aiPackageVersion};break;case`pnpm`:break}return n}async function patchWorkspaceRootPackageJson(e,t,n){if(!isPackageManagerWorkspaceMember(e,t))return{changed:!1};let r=workspaceRootPackageJsonPath(e,t);if(r===void 0||!existsSync(r))return{changed:!1};let i=await patchPackageJson(r,packageManagerRootOnlyPackageJsonPatch(e,n));return i.changed&&await n.onWorkspaceRootMutation?.({kind:`package-json`,nodeEngineOverride:i.nodeEngineOverride,path:r}),{...i,path:r}}async function applyPackageManagerWorkspaceConfiguration(t){let n=resolve(t.workspaceProbeRoot??t.projectRoot),r=t.packageManager===`pnpm`?void 0:await ensurePackageManagerWorkspaceIncludesProject(t.packageManager,n),i=await getPackageManagerStrategy(t.packageManager).applyProjectConfiguration(t.projectRoot,{workspaceProbeRoot:n}),o=[...r===void 0?[]:[r],...i.filesWritten],s=[...i.filesSkipped];for(let n of o)isPathInside(t.projectRoot,n)||await t.onWorkspaceRootMutation?.({kind:`workspace-config`,path:n});return{filesSkipped:s,filesWritten:o}}export{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson};
@@ -37,7 +37,7 @@ export interface ArgsHeadlessProject {
37
37
  skipVercel?: boolean;
38
38
  /** Vercel team slug (scope). Undefined = the current vercel scope. */
39
39
  team?: string;
40
- /** Existing project slug to link. Undefined = create a new project named after the agent. */
40
+ /** Existing project name or ID to link. Undefined = create a project named after the agent. */
41
41
  project?: string;
42
42
  }
43
43
  /**
@@ -61,6 +61,11 @@ export type ProvisioningMode = {
61
61
  } | {
62
62
  headless: false;
63
63
  };
64
+ /** Stable identity of an existing Vercel project selected for linking. */
65
+ export interface VercelProjectIdentity {
66
+ projectId: string;
67
+ projectName: string;
68
+ }
64
69
  /**
65
70
  * Resolved project decision, fully concrete. The variant determines whether
66
71
  * the flow creates a project, links an existing one, or avoids Vercel project
@@ -74,7 +79,7 @@ export type ResolvedVercelProject = {
74
79
  team: string;
75
80
  } | {
76
81
  kind: "existing";
77
- project: string;
82
+ project: VercelProjectIdentity;
78
83
  team: string;
79
84
  };
80
85
  /** A concrete project to ensure exists and link. */
@@ -0,0 +1,9 @@
1
+ import type { VercelCaptureFailure, VercelCaptureResult } from "#setup/primitives/index.js";
2
+ /** Treats a structured API error as a failure even when `vercel api --raw` exits zero. */
3
+ export declare function normalizeVercelApiResult(result: VercelCaptureResult): VercelCaptureResult;
4
+ /** Whether a Vercel API failure proves that the requested resource does not exist. */
5
+ export declare function isNotFoundApiFailure(failure: VercelCaptureFailure): boolean;
6
+ /** Whether a Vercel API failure proves that the requested resource already exists. */
7
+ export declare function isConflictApiFailure(failure: VercelCaptureFailure): boolean;
8
+ /** Whether a scoped Vercel API request was denied. */
9
+ export declare function isForbiddenApiFailure(failure: VercelCaptureFailure): boolean;
@@ -0,0 +1 @@
1
+ import{number,object,string,union}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";const VercelApiErrorSchema=object({error:object({code:union([string(),number()]).optional(),message:string().optional()}).optional()});function apiErrorFromStdout(e){try{let t=VercelApiErrorSchema.safeParse(JSON.parse(e));return t.success?t.data.error:void 0}catch{return}}function apiFailureText(e){let t=apiErrorFromStdout(e.stdout);return`${String(t?.code??``)} ${t?.message??``} ${e.stderr}`.toLowerCase()}function normalizeVercelApiResult(e){if(!e.ok)return e;let t=apiErrorFromStdout(e.stdout);if(t===void 0)return e;let n=t.message??(t.code===void 0?void 0:String(t.code));return{ok:!1,failure:{stdout:e.stdout,stderr:``,message:`Vercel API request failed${n===void 0?``:`: ${n}`}.`}}}function isNotFoundApiFailure(e){return/(^|\W)(404|not_found)(\W|$)|not found/.test(apiFailureText(e))}function isConflictApiFailure(e){return/(^|\W)(409|conflict)(\W|$)|already exists/.test(apiFailureText(e))}function isForbiddenApiFailure(e){return/(^|\W)(403|forbidden|not_authorized|team_unauthorized|sso|saml)(\W|$)|not authorized/.test(apiFailureText(e))}export{isConflictApiFailure,isForbiddenApiFailure,isNotFoundApiFailure,normalizeVercelApiResult};
@@ -1 +1 @@
1
- import{number,object,string,union}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{projectReferenceFromEnvironment,readProjectLink}from"#setup/project-resolution.js";import{captureVercel}from"#setup/primitives/index.js";const VercelDeploymentSchema=object({projectId:string().min(1),name:string().min(1),target:string().nullable().optional(),customEnvironment:object({slug:string().min(1)}).nullable().optional()}),VercelApiErrorSchema=object({error:object({code:union([string(),number()]).optional(),message:string().optional()})}),verifiedVercelTargetBrand=Symbol(`VerifiedVercelTarget`),defaultDeps={captureVercel,readProjectLink};function isNotFoundApiFailure(e){if(e.code===404)return!0;try{let t=VercelApiErrorSchema.safeParse(JSON.parse(e.stdout));if(!t.success)return!1;let n=String(t.data.error.code??``).toLowerCase(),r=t.data.error.message?.toLowerCase()??``;return n===`404`||n===`not_found`||r.includes(`not found`)}catch{return!1}}function environmentForDeployment(e){return e.customEnvironment!==null&&e.customEnvironment!==void 0?e.customEnvironment.slug:e.target===`production`?`production`:`preview`}async function resolveVercelDeployment(e){let t={...defaultDeps,...e.deps},n=e.source??projectReferenceFromEnvironment(process.env)??await t.readProjectLink(e.workspaceRoot);if(n===void 0)return{kind:`unscoped`};let r=await t.captureVercel([`api`,`/v13/deployments/${encodeURIComponent(e.host)}`,`--scope`,n.orgId,`--raw`],{cwd:e.workspaceRoot,nonInteractive:!0,signal:e.signal,timeoutMs:1e4});if(!r.ok)return e.signal?.aborted===!0||r.failure.errno===`ABORT_ERR`?{kind:`cancelled`}:isNotFoundApiFailure(r.failure)?{kind:`not-found`}:{kind:`failed`,failure:{cause:`vercel`,failure:r.failure}};let i;try{i=JSON.parse(r.stdout)}catch{return{kind:`failed`,failure:{cause:`invalid-json`,message:`Vercel returned invalid deployment JSON.`}}}let a=VercelDeploymentSchema.safeParse(i);if(!a.success)return{kind:`failed`,failure:{cause:`invalid-shape`,message:`Vercel returned an invalid deployment response.`}};if(a.data.projectId!==n.projectId)return{kind:`project-mismatch`,expectedProjectId:n.projectId,actualProjectId:a.data.projectId};let o=`https://${new URL(`https://${e.host}`).host}`;return{kind:`resolved`,target:{[verifiedVercelTargetBrand]:!0,origin:o,deployment:{provider:`vercel`,ownerId:n.orgId,projectId:a.data.projectId,projectName:a.data.name,environment:environmentForDeployment(a.data)}}}}export{resolveVercelDeployment};
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{isNotFoundApiFailure,normalizeVercelApiResult}from"./vercel-api-failure.js";import{projectReferenceFromEnvironment,readProjectLink}from"#setup/project-resolution.js";import{captureVercel}from"#setup/primitives/index.js";const VercelDeploymentSchema=object({projectId:string().min(1),name:string().min(1),target:string().nullable().optional(),customEnvironment:object({slug:string().min(1)}).nullable().optional()}),verifiedVercelTargetBrand=Symbol(`VerifiedVercelTarget`),defaultDeps={captureVercel,readProjectLink};function environmentForDeployment(e){return e.customEnvironment!==null&&e.customEnvironment!==void 0?e.customEnvironment.slug:e.target===`production`?`production`:`preview`}async function resolveVercelDeployment(e){let t={...defaultDeps,...e.deps},n=e.source??projectReferenceFromEnvironment(process.env)??await t.readProjectLink(e.workspaceRoot);if(n===void 0)return{kind:`unscoped`};let r=normalizeVercelApiResult(await t.captureVercel([`api`,`/v13/deployments/${encodeURIComponent(e.host)}`,`--scope`,n.orgId,`--raw`],{cwd:e.workspaceRoot,nonInteractive:!0,signal:e.signal,timeoutMs:1e4}));if(!r.ok)return e.signal?.aborted===!0||r.failure.errno===`ABORT_ERR`?{kind:`cancelled`}:isNotFoundApiFailure(r.failure)?{kind:`not-found`}:{kind:`failed`,failure:{cause:`vercel`,failure:r.failure}};let i;try{i=JSON.parse(r.stdout)}catch{return{kind:`failed`,failure:{cause:`invalid-json`,message:`Vercel returned invalid deployment JSON.`}}}let a=VercelDeploymentSchema.safeParse(i);if(!a.success)return{kind:`failed`,failure:{cause:`invalid-shape`,message:`Vercel returned an invalid deployment response.`}};if(a.data.projectId!==n.projectId)return{kind:`project-mismatch`,expectedProjectId:n.projectId,actualProjectId:a.data.projectId};let o=`https://${new URL(`https://${e.host}`).host}`;return{kind:`resolved`,target:{[verifiedVercelTargetBrand]:!0,origin:o,deployment:{provider:`vercel`,ownerId:n.orgId,projectId:a.data.projectId,projectName:a.data.name,environment:environmentForDeployment(a.data)}}}}export{resolveVercelDeployment};
@@ -0,0 +1,25 @@
1
+ import { type VercelCaptureFailure } from "#setup/primitives/index.js";
2
+ import { z } from "zod";
3
+ import type { VercelProjectOperationOptions } from "./project-resolution.js";
4
+ export type { VercelProjectOperationOptions };
5
+ declare const VercelTeamListEntrySchema: z.ZodObject<{
6
+ name: z.ZodString;
7
+ slug: z.ZodString;
8
+ current: z.ZodBoolean;
9
+ }, z.core.$strip>;
10
+ type VercelTeamListEntry = z.infer<typeof VercelTeamListEntrySchema>;
11
+ declare const VercelProjectListEntrySchema: z.ZodObject<{
12
+ name: z.ZodString;
13
+ id: z.ZodString;
14
+ }, z.core.$strip>;
15
+ export type VercelProjectListEntry = z.infer<typeof VercelProjectListEntrySchema>;
16
+ /** Parses one JSON response captured from the Vercel CLI. */
17
+ export declare function parseVercelJson(stdout: string, description: string): unknown;
18
+ /** Converts a scoped API denial into the Vercel re-authentication action. */
19
+ export declare function requireVercelTeamAccess(failure: VercelCaptureFailure): never;
20
+ /** Lists every Vercel scope available to the current CLI user. */
21
+ export declare function listTeams(projectRoot: string, options?: VercelProjectOperationOptions): Promise<VercelTeamListEntry[]>;
22
+ /** Lists the 20 most recent Vercel projects in one account scope. */
23
+ export declare function listRecentProjects(projectRoot: string, team: string, options?: VercelProjectOperationOptions): Promise<VercelProjectListEntry[]>;
24
+ /** Searches every matching Vercel project page in one account scope. */
25
+ export declare function searchProjects(projectRoot: string, team: string, query: string, options?: VercelProjectOperationOptions): Promise<VercelProjectListEntry[]>;
@@ -0,0 +1 @@
1
+ import{array,boolean,number,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{isForbiddenApiFailure}from"./vercel-api-failure.js";import{captureVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";const VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelPaginationSchema=object({next:number().int().nonnegative().nullable().optional()}),VercelTeamPageSchema=object({teams:array(VercelTeamListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.teams,next:e.pagination?.next??void 0})),VercelProjectPageSchema=object({projects:array(VercelProjectListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.projects,next:e.pagination?.next??void 0}));function parseVercelJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}async function drainPages(e,t,n){let r=new Map,i=new Set,a;for(;;){let o=await n(a);for(let e of o.items)r.set(t(e),e);if(o.next===void 0)return[...r.values()];if(i.has(o.next))throw Error(`Vercel returned a repeated pagination cursor for ${e}.`);i.add(o.next),a=o.next}}function requireVercelTeamAccess(e){let t=e.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-forbidden`,command:`vercel login`,reason:`Vercel denied access to this scope.${t?` ${t}`:``} Re-authenticate (for example to complete a team's SSO) or switch to a team you can access.`})}async function fetchTeamPage(e,t,n){let r=[`teams`,`ls`,`--format`,`json`];n!==void 0&&r.push(`--next`,String(n));let i=await captureVercel(r,{cwd:e,signal:t.signal});if(t.signal?.throwIfAborted(),!i.ok)throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not list Vercel teams. ${i.failure.message}`);let s=VercelTeamPageSchema.safeParse(parseVercelJson(i.stdout,`teams`));if(!s.success)throw Error(`Could not read teams from Vercel CLI JSON output.`);return s.data}async function listTeams(e,t={}){return drainPages(`Vercel teams`,e=>e.slug,n=>fetchTeamPage(e,t,n))}async function fetchProjectPage(e,t,n){let r=[`project`,`ls`,`--format`,`json`,`--scope`,t];n.search!==void 0&&r.push(`--filter`,n.search),n.next!==void 0&&r.push(`--next`,String(n.next));let i=await captureVercel(r,{cwd:e,signal:n.signal,timeoutMs:15e3});if(n.signal?.throwIfAborted(),!i.ok)throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not list Vercel projects in ${t}. ${i.failure.message}`);let s=VercelProjectPageSchema.safeParse(parseVercelJson(i.stdout,`projects`));if(!s.success)throw Error(`Could not read projects from Vercel CLI JSON output.`);return s.data}async function listRecentProjects(e,t,n={}){return(await fetchProjectPage(e,t,n)).items}async function searchProjects(e,t,n,r={}){let i=n.trim();if(i.length===0)throw Error(`Project search query cannot be empty.`);return drainPages(`project search in ${t}`,e=>e.id,n=>fetchProjectPage(e,t,{...r,search:i,next:n}))}export{listRecentProjects,listTeams,parseVercelJson,requireVercelTeamAccess,searchProjects};
@@ -1,47 +1,18 @@
1
1
  import { createPromptCommandOutput } from "#setup/cli/index.js";
2
2
  import { type VercelCaptureFailure } from "#setup/primitives/index.js";
3
- import { z } from "zod";
4
- import type { ProjectResolution } from "./project-resolution.js";
3
+ import { type ProjectResolution } from "./project-resolution.js";
5
4
  import type { Prompter } from "./prompter.js";
6
- import type { ResolvedVercelProjectSpec } from "./state.js";
7
- declare const VercelTeamListEntrySchema: z.ZodObject<{
8
- name: z.ZodString;
9
- slug: z.ZodString;
10
- current: z.ZodBoolean;
11
- }, z.core.$strip>;
12
- type VercelTeamListEntry = z.infer<typeof VercelTeamListEntrySchema>;
13
- declare const VercelProjectListEntrySchema: z.ZodObject<{
14
- name: z.ZodString;
15
- id: z.ZodString;
16
- }, z.core.$strip>;
17
- type VercelProjectListEntry = z.infer<typeof VercelProjectListEntrySchema>;
18
- declare const VercelProjectReferenceSchema: z.ZodObject<{
19
- id: z.ZodString;
20
- name: z.ZodString;
21
- }, z.core.$strip>;
22
- type VercelProjectReference = z.infer<typeof VercelProjectReferenceSchema>;
23
- export interface VercelProjectOperationOptions {
24
- signal?: AbortSignal;
25
- }
5
+ import type { ResolvedVercelProjectSpec, VercelProjectIdentity } from "./state.js";
6
+ import { type VercelProjectOperationOptions } from "./vercel-project-api.js";
26
7
  export interface PickProjectOptions extends VercelProjectOperationOptions {
27
8
  /** Whether an empty project list may fall back to entering a name to create. */
28
9
  allowCreateWhenEmpty?: boolean;
29
10
  }
30
11
  export declare function unresolvedProject(): ProjectResolution;
31
- /**
32
- * Runs a network reach behind a section-like spinner so the user sees the CLI
33
- * is working, not hung. The spinner clears whether the work resolves or throws,
34
- * and degrades to nothing when the prompter has no spinner (headless/test).
35
- */
36
- export declare function withNetworkSpinner<T>(prompter: Prompter, message: string, task: () => Promise<T>): Promise<T>;
37
12
  /** Resolves the linked project id from a resolution, if any. */
38
13
  export declare function projectIdFromResolution(project: ProjectResolution): string | undefined;
39
- /** Lists the Vercel scopes available to the current CLI user. */
40
- export declare function listTeams(projectRoot: string, options?: VercelProjectOperationOptions): Promise<VercelTeamListEntry[]>;
41
- /** Lists Vercel projects available under an explicit team or personal scope. */
42
- export declare function listProjects(projectRoot: string, team: string, options?: VercelProjectOperationOptions): Promise<VercelProjectListEntry[]>;
43
14
  /** Resolves one project by exact name or id through the Vercel API. */
44
- export declare function resolveProjectByNameOrId(projectRoot: string, team: string, projectNameOrId: string, options?: VercelProjectOperationOptions): Promise<VercelProjectReference | null>;
15
+ export declare function resolveProjectByNameOrId(projectRoot: string, team: string, projectNameOrId: string, options?: VercelProjectOperationOptions): Promise<VercelProjectIdentity | null>;
45
16
  export declare function assertNewProjectNameAvailable(projectRoot: string, team: string, projectName: string, options?: VercelProjectOperationOptions): Promise<void>;
46
17
  /**
47
18
  * Throws the login action. When the underlying `vercel whoami` failure is
@@ -66,15 +37,9 @@ export declare function requireVercelAuth(failure: VercelCaptureFailure): never;
66
37
  * not-authenticated diagnostic.
67
38
  */
68
39
  export type VercelAuthStatus = "authenticated" | "logged-out" | "cli-missing" | "unavailable";
40
+ /** Returns the user-facing reason Vercel-backed setup is unavailable. */
41
+ export declare function vercelAuthBlockerReason(authStatus: VercelAuthStatus): string | undefined;
69
42
  export declare function getVercelAuthStatus(projectRoot: string, options?: VercelProjectOperationOptions): Promise<VercelAuthStatus>;
70
- /**
71
- * Throws the re-auth action for a forbidden scope. The session is logged in,
72
- * but a scoped command was denied — the SSO/SAML or non-membership case. The
73
- * remedy is still `vercel login` (it completes the team's SSO and refreshes the
74
- * token), so the dev TUI routes it to `/login`; the reason names the membership
75
- * fallback for the case re-auth cannot fix.
76
- */
77
- export declare function requireVercelTeamAccess(failure: VercelCaptureFailure): never;
78
43
  /**
79
44
  * Ensures Vercel authentication before any provisioning. `vercel whoami` exits
80
45
  * non-zero when not logged in; any other failure (a missing CLI, a transient
@@ -110,25 +75,14 @@ export declare function validateTeam(prompter: Prompter, projectRoot: string, te
110
75
  * user filters and chooses from the list with a single-selection picker.
111
76
  */
112
77
  export declare function pickTeam(prompter: Prompter, projectRoot: string, presetTeam: string | undefined, options?: VercelProjectOperationOptions): Promise<string>;
113
- /**
114
- * A picked Vercel project. `exists` distinguishes a project the user selected
115
- * from the existing list (link it) from a name they typed because none exist
116
- * yet (create it), so the caller can build the right `new` vs `existing` plan.
117
- */
118
- export interface ArgsPickedProject {
119
- /** Project slug: an existing project's name, or a name to create. */
120
- project: string;
121
- /** True for a selected existing project; false for a typed-in name to create. */
122
- exists: boolean;
123
- }
124
78
  /** Picks an existing project under a team, or a name to create when none exist. */
125
- export declare function pickProject(prompter: Prompter, projectRoot: string, team: string, options?: PickProjectOptions): Promise<ArgsPickedProject>;
79
+ export declare function pickProject(prompter: Prompter, projectRoot: string, team: string, options?: PickProjectOptions): Promise<ResolvedVercelProjectSpec>;
126
80
  /** Returns a project name for a new Vercel project, prompting when the default exists. */
127
81
  export declare function pickNewProjectName(prompter: Prompter, projectRoot: string, team: string, defaultProjectName: string, options?: VercelProjectOperationOptions): Promise<string>;
128
82
  /**
129
83
  * Ensures the concrete project exists (creating it for a `new` plan) and links
130
- * this directory to it. Pure executor: it acts on a fully-resolved spec and
131
- * never prompts for a team or project. Returns whether the link succeeded.
84
+ * this directory to it. Acts on a fully-resolved spec — never prompts for a
85
+ * team or project. Returns the linked project, or undefined if `vercel link`
86
+ * did not complete.
132
87
  */
133
- export declare function linkProject(prompter: Prompter, projectRoot: string, spec: ResolvedVercelProjectSpec, onOutput: ReturnType<typeof createPromptCommandOutput>, options?: VercelProjectOperationOptions): Promise<boolean>;
134
- export {};
88
+ export declare function linkProject(prompter: Prompter, projectRoot: string, spec: ResolvedVercelProjectSpec, onOutput: ReturnType<typeof createPromptCommandOutput>, options?: VercelProjectOperationOptions): Promise<VercelProjectIdentity | undefined>;
@@ -1 +1 @@
1
- import{__toESM}from"../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{boolean,object,record,string,unknown}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";import{hasVercelHostFramework}from"#setup/scaffold/index.js";import{captureVercel,runVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{whimsyFor}from"#setup/cli/index.js";var import_picocolors=__toESM(require_picocolors(),1);const JsonObjectSchema=record(string(),unknown()),VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelProjectReferenceSchema=object({id:string(),name:string()}),VercelApiErrorSchema=object({error:object({code:string().optional(),message:string().optional()}).optional()});function unresolvedProject(){return{kind:`unresolved`}}async function withNetworkSpinner(e,t,n){let r=e.log.spinner?.(t);try{return await n()}finally{r?.stop()}}function projectIdFromResolution(e){return e.kind===`unresolved`?void 0:e.projectId}function parseJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}function parseVercelJsonList(e,t,n){let r=parseJson(e,t),i=JsonObjectSchema.safeParse(r);if(!i.success)throw Error(`Could not read ${t} from Vercel CLI JSON output.`);let a=i.data[t];if(!Array.isArray(a))throw Error(`Vercel CLI JSON output did not include ${t}.`);let o=[];for(let e of a){let t=n.safeParse(e);t.success&&o.push(t.data)}return o}function parseTeamList(e){return e===void 0?[]:parseVercelJsonList(e,`teams`,VercelTeamListEntrySchema)}async function listTeams(e,t={}){let n=await captureVercel([`teams`,`ls`,`--format`,`json`],{cwd:e,signal:t.signal});if(!n.ok)throw isForbiddenApiFailure(n.failure)&&requireVercelTeamAccess(n.failure),Error(`Could not list Vercel teams. ${n.failure.message}`);return parseTeamList(n.stdout)}function parseProjectList(e){return e===void 0?[]:parseVercelJsonList(e,`projects`,VercelProjectListEntrySchema)}async function listProjects(e,t,n={}){let r=await captureVercel([`project`,`ls`,`--format`,`json`,`--scope`,t],{cwd:e,signal:n.signal});if(!r.ok)throw isForbiddenApiFailure(r.failure)&&requireVercelTeamAccess(r.failure),Error(`Could not list Vercel projects in ${t}. ${r.failure.message}`);return parseProjectList(r.stdout)}function isNotFoundApiFailure(e){if(e.code===404)return!0;let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stderr}`.toLowerCase();return r.includes(`not_found`)||r.includes(`not found`)||r.includes(`404`)}function isConflictApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`409`)||r.includes(`conflict`)||r.includes(`already exists`)}function isForbiddenApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`403`)||r.includes(`forbidden`)||r.includes(`not_authorized`)||r.includes(`not authorized`)||r.includes(`sso`)||r.includes(`saml`)}function safeParseJson(e){try{return JSON.parse(e)}catch{return}}function parseProjectReference(e,t){let n=VercelProjectReferenceSchema.safeParse(parseJson(e,t));if(!n.success)throw Error(`Could not read Vercel project identity from ${t}.`);return n.data}async function resolveProjectByNameOrId(e,t,n,r={}){let i=await captureVercel([`api`,`/v9/projects/${encodeURIComponent(n)}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal});if(i.ok)return parseProjectReference(i.stdout,`project ${n}`);if(isNotFoundApiFailure(i.failure))return null;throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not resolve project "${n}" in ${t}. ${i.failure.message}`)}async function createProject(e,t,n,r,i){let a=[`api`,`/v10/projects`,`--scope`,t,`--method`,`POST`,`--raw-field`,`name=${n}`];await hasVercelHostFramework(e)||a.push(`--raw-field`,`framework=eve`),a.push(`--raw`);let o=await captureVercel(a,{cwd:e,onOutput:r,signal:i.signal});if(o.ok)return parseProjectReference(o.stdout,`created project ${n}`);throw isConflictApiFailure(o.failure)?Error(projectNameCollisionMessage(n,t)):(isForbiddenApiFailure(o.failure)&&requireVercelTeamAccess(o.failure),Error(`Could not create Vercel project "${n}" in ${t}. ${o.failure.message}`))}function projectNameCollisionMessage(e,t){return`Vercel project "${e}" already exists in ${t}. Pass --project ${e} to link it, or choose a different project name.`}async function assertNewProjectNameAvailable(e,t,n,r={}){if(await resolveProjectByNameOrId(e,t,n,r)!==null)throw Error(projectNameCollisionMessage(n,t))}function requireVercelLogin(e){let t=`Provisioning a Vercel project requires you to be logged in to Vercel.`,n=e?.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-login`,command:`vercel login`,reason:e?`${t} The Vercel CLI check did not succeed: ${e.message}${n?` ${n}`:``}`:t})}function probeWhoami(e,t){return captureVercel([`whoami`],{cwd:e,signal:t.signal,timeoutMs:1e4})}function isLoggedOutFailure(e){let t=`${e.stdout} ${e.stderr}`.toLowerCase();return t.includes(`credentials`)||t.includes(`not authenticated`)||t.includes(`not logged in`)}function requireVercelAuth(e){if(e.errno===`ENOENT`)throw new HumanActionRequiredError({kind:`vercel-cli-missing`,command:`npm i -g vercel@latest`,reason:e.message});isLoggedOutFailure(e)&&requireVercelLogin(e);let t=e.stderr.trim();throw Error(`Couldn't verify your Vercel login: ${e.message}${t?` ${t}`:``}`)}async function getVercelAuthStatus(e,t={}){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok?`authenticated`:n.failure.errno===`ENOENT`?`cli-missing`:isLoggedOutFailure(n.failure)?`logged-out`:`unavailable`}function requireVercelTeamAccess(e){let t=e.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-forbidden`,command:`vercel login`,reason:`Vercel denied access to this scope.${t?` ${t}`:``} Re-authenticate (for example to complete a team's SSO) or switch to a team you can access.`})}async function requireAuth(e,t,n={}){let check=async()=>{let t=await probeWhoami(e,n);n.signal?.throwIfAborted(),t.ok||requireVercelAuth(t.failure)};if(t===void 0){await check();return}await withNetworkSpinner(t,whimsyFor(`auth`),check)}async function isVercelAuthenticated(e,t={}){return await getVercelAuthStatus(e,t)===`authenticated`}async function whoamiScope(e,t){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok||requireVercelAuth(n.failure),n.stdout.trim()}async function resolveTeam(e,t,n={}){return t===void 0?(await listTeams(e,n)).find(e=>e.current)?.slug??await whoamiScope(e,n):t}async function validateTeam(e,t,n,r={}){if(n===void 0)return;let i=await listTeams(t,r);if(i.length>0&&!i.some(e=>e.slug===n))throw Error(`Team "${n}" was not found in \`vercel teams ls\`. Pass a valid team slug or omit --team.`)}async function pickTeam(e,t,n,r={}){if(n!==void 0)return await validateTeam(e,t,n,r),resolveTeam(t,n,r);let i=await withNetworkSpinner(e,whimsyFor(`teams`),()=>listTeams(t,r));return i.length<=1?i.find(e=>e.current)?.slug??await whoamiScope(t,r):e.select({message:`Select your team`,search:!0,placeholder:`type to search teams`,options:i.map(e=>({value:e.slug,label:e.current?`${e.name} (current)`:e.name})),initialValue:i.find(e=>e.current)?.slug})}async function pickProject(e,t,n,r={}){let i=await withNetworkSpinner(e,whimsyFor(`projects`,n),()=>listProjects(t,n,r));if(i.length===0){if(r.allowCreateWhenEmpty===!1)throw Error(`No existing Vercel projects found in ${n}. Create one in Vercel, then try again.`);return{project:await e.text({message:`No projects found in ${n}. Enter a project name to create`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}),exists:!1}}return{project:await e.select({message:`Project to link`,search:!0,placeholder:`type to filter projects`,options:i.map(e=>({value:e.name,label:e.name}))}),exists:!0}}async function pickNewProjectName(e,t,n,r,i={}){let a=await withNetworkSpinner(e,whimsyFor(`project-name`,n),()=>resolveProjectByNameOrId(t,n,r.trim(),i)),o=r.trim();for(;a!==null;)o=(await e.text({message:`New project name`,defaultValue:`${o}-2`,notices:[{tone:`warning`,text:`${import_picocolors.default.yellow(`Project named`)} '${import_picocolors.default.blue(o)}' ${import_picocolors.default.yellow(`already exists in`)} '${import_picocolors.default.blue(n)}'`}],validate:e=>{if(e.trim().length===0)return`Project name cannot be empty.`}})).trim(),a=await resolveProjectByNameOrId(t,n,o,i);return o}async function linkProject(e,t,n,r,i={}){let a=[`--scope`,n.team],o;if(n.kind===`new`)o=await withNetworkSpinner(e,`Creating Vercel project "${n.project}" in ${n.team}...`,async()=>(await assertNewProjectNameAvailable(t,n.team,n.project,i),createProject(t,n.team,n.project,r,i)));else{let e=await resolveProjectByNameOrId(t,n.team,n.project,i);if(e===null)throw Error(`Vercel project "${n.project}" was not found in ${n.team}.`);o=e}return withNetworkSpinner(e,`Linking this directory to Vercel project "${o.name}"...`,()=>runVercel([`link`,`--project`,o.id,...a,`--yes`],{cwd:t,onOutput:r,nonInteractive:!0,signal:i.signal}))}export{assertNewProjectNameAvailable,getVercelAuthStatus,isVercelAuthenticated,linkProject,listProjects,listTeams,pickNewProjectName,pickProject,pickTeam,projectIdFromResolution,requireAuth,requireVercelAuth,requireVercelLogin,requireVercelTeamAccess,resolveProjectByNameOrId,resolveTeam,unresolvedProject,validateTeam,withNetworkSpinner};
1
+ import{__toESM}from"../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{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{assertNoLegacyProjectLinkDirectory}from"./project-resolution.js";import{withSpinner}from"./with-spinner.js";import{isConflictApiFailure,isForbiddenApiFailure,isNotFoundApiFailure,normalizeVercelApiResult}from"./vercel-api-failure.js";import{listRecentProjects,listTeams,parseVercelJson,requireVercelTeamAccess,searchProjects}from"./vercel-project-api.js";import{hasVercelHostFramework}from"#setup/scaffold/index.js";import{captureVercel,runVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{whimsyFor}from"#setup/cli/index.js";var import_picocolors=__toESM(require_picocolors(),1);const VercelProjectReferenceSchema=object({id:string().min(1),name:string().min(1)});function unresolvedProject(){return{kind:`unresolved`}}function projectIdFromResolution(e){return e.kind===`unresolved`?void 0:e.projectId}function parseProjectReference(e,t){let n=VercelProjectReferenceSchema.safeParse(parseVercelJson(e,t));if(!n.success)throw Error(`Could not read Vercel project identity from ${t}.`);return{projectId:n.data.id,projectName:n.data.name}}async function resolveProjectByNameOrId(e,t,n,r={}){let i=normalizeVercelApiResult(await captureVercel([`api`,`/v9/projects/${encodeURIComponent(n)}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal}));if(i.ok)return parseProjectReference(i.stdout,`project ${n}`);if(isNotFoundApiFailure(i.failure))return null;throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not resolve project "${n}" in ${t}. ${i.failure.message}`)}async function createProject(e,t,n,r,i){let a=[`api`,`/v10/projects`,`--scope`,t,`--method`,`POST`,`--raw-field`,`name=${n}`];await hasVercelHostFramework(e)||a.push(`--raw-field`,`framework=eve`),a.push(`--raw`);let c=normalizeVercelApiResult(await captureVercel(a,{cwd:e,onOutput:r,signal:i.signal}));if(c.ok)return parseProjectReference(c.stdout,`created project ${n}`);throw isConflictApiFailure(c.failure)?Error(projectNameCollisionMessage(n,t)):(isForbiddenApiFailure(c.failure)&&requireVercelTeamAccess(c.failure),Error(`Could not create Vercel project "${n}" in ${t}. ${c.failure.message}`))}function projectNameCollisionMessage(e,t){return`Vercel project "${e}" already exists in ${t}. Pass --project ${e} to link it, or choose a different project name.`}async function assertNewProjectNameAvailable(e,t,n,r={}){if(await resolveProjectByNameOrId(e,t,n,r)!==null)throw Error(projectNameCollisionMessage(n,t))}function requireVercelLogin(e){let t=`Provisioning a Vercel project requires you to be logged in to Vercel.`,n=e?.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-login`,command:`vercel login`,reason:e?`${t} The Vercel CLI check did not succeed: ${e.message}${n?` ${n}`:``}`:t})}function probeWhoami(e,t){return captureVercel([`whoami`],{cwd:e,signal:t.signal,timeoutMs:1e4})}function isLoggedOutFailure(e){let t=`${e.stdout} ${e.stderr}`.toLowerCase();return t.includes(`credentials`)||t.includes(`not authenticated`)||t.includes(`not logged in`)}function requireVercelAuth(e){if(e.errno===`ENOENT`)throw new HumanActionRequiredError({kind:`vercel-cli-missing`,command:`npm i -g vercel@latest`,reason:e.message});isLoggedOutFailure(e)&&requireVercelLogin(e);let t=e.stderr.trim();throw Error(`Couldn't verify your Vercel login: ${e.message}${t?` ${t}`:``}`)}function vercelAuthBlockerReason(e){switch(e){case`authenticated`:return;case`cli-missing`:return`Vercel CLI not found, see /vc`;case`logged-out`:return`Log in to Vercel first, see /login`;case`unavailable`:return`Couldn't reach Vercel, check your connection`;default:return e}}async function getVercelAuthStatus(e,t={}){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok?`authenticated`:n.failure.errno===`ENOENT`?`cli-missing`:isLoggedOutFailure(n.failure)?`logged-out`:`unavailable`}async function requireAuth(e,t,n={}){let check=async()=>{let t=await probeWhoami(e,n);n.signal?.throwIfAborted(),t.ok||requireVercelAuth(t.failure)};if(t===void 0){await check();return}await withSpinner(t,whimsyFor(`auth`),check)}async function isVercelAuthenticated(e,t={}){return await getVercelAuthStatus(e,t)===`authenticated`}async function whoamiScope(e,t){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok||requireVercelAuth(n.failure),n.stdout.trim()}async function resolveTeam(e,t,n={}){return t===void 0?(await listTeams(e,n)).find(e=>e.current)?.slug??await whoamiScope(e,n):t}async function validateTeam(e,t,n,r={}){if(n===void 0)return;let i=await listTeams(t,r);if(i.length>0&&!i.some(e=>e.slug===n))throw Error(`Team "${n}" was not found in \`vercel teams ls\`. Pass a valid team slug or omit --team.`)}async function pickTeam(e,t,n,r={}){if(n!==void 0)return await validateTeam(e,t,n,r),resolveTeam(t,n,r);let i=await withSpinner(e,whimsyFor(`teams`),()=>listTeams(t,r));return i.length<=1?i.find(e=>e.current)?.slug??await whoamiScope(t,r):e.select({message:`Select your team`,search:!0,placeholder:`type to search teams`,options:i.map(e=>({value:e.slug,label:e.current?`${e.name} (current)`:e.name})),initialValue:i.find(e=>e.current)?.slug})}const SEARCH_PROJECT_PREFIX=`\0search-project:`;function appendProjects(e,t){return[...new Map([...e,...t].map(e=>[e.id,e])).values()]}async function pickProject(e,t,n,r={}){let i=await withSpinner(e,whimsyFor(`projects`,n),()=>listRecentProjects(t,n,r));if(i.length===0){if(r.allowCreateWhenEmpty===!1)throw Error(`No existing Vercel projects found in ${n}. Create one in Vercel, then try again.`);return{kind:`new`,project:await e.text({message:`No projects found in ${n}. Enter a project name to create`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}),team:n}}for(;;){let o=await e.select({message:`Project to link`,search:!0,placeholder:`type to filter projects`,searchAction:{label:e=>`Search for '${e}'`,value:e=>`${SEARCH_PROJECT_PREFIX}${e}`,load:async e=>{let a=await searchProjects(t,n,e,{signal:r.signal});return i=appendProjects(i,a),i.map(e=>({value:e.id,label:e.name}))}},options:i.map(e=>({value:e.id,label:e.name})),initialValue:i[0]?.id}),s=o.startsWith(SEARCH_PROJECT_PREFIX)?o.slice(16):void 0;if(s===void 0){let e=i.find(e=>e.id===o);if(e===void 0)throw Error(`Selected Vercel project is not available.`);return{kind:`existing`,project:{projectId:e.id,projectName:e.name},team:n}}let c=await withSpinner(e,`Searching ${n} for "${s}"...`,()=>searchProjects(t,n,s,{signal:r.signal}));if(c.length===0){e.note(`No projects matched "${s}" in ${n}.`);continue}i=appendProjects(i,c)}}async function pickNewProjectName(e,t,n,r,i={}){let o=await withSpinner(e,whimsyFor(`project-name`,n),()=>resolveProjectByNameOrId(t,n,r.trim(),i)),s=r.trim();for(;o!==null;)s=(await e.text({message:`New project name`,defaultValue:`${s}-2`,notices:[{tone:`warning`,text:`${import_picocolors.default.yellow(`Project named`)} '${import_picocolors.default.blue(s)}' ${import_picocolors.default.yellow(`already exists in`)} '${import_picocolors.default.blue(n)}'`}],validate:e=>{if(e.trim().length===0)return`Project name cannot be empty.`}})).trim(),o=await resolveProjectByNameOrId(t,n,s,i);return s}async function linkProject(e,t,n,r,o={}){await assertNoLegacyProjectLinkDirectory(t);let s=[`--scope`,n.team],c;return c=n.kind===`new`?await withSpinner(e,`Creating Vercel project "${n.project}" in ${n.team}...`,async()=>(await assertNewProjectNameAvailable(t,n.team,n.project,o),createProject(t,n.team,n.project,r,o))):n.project,await withSpinner(e,`Linking this directory to Vercel project "${c.projectName}"...`,()=>runVercel([`link`,`--project`,c.projectId,...s,`--yes`],{cwd:t,onOutput:r,nonInteractive:!0,signal:o.signal}))?c:void 0}export{assertNewProjectNameAvailable,getVercelAuthStatus,isVercelAuthenticated,linkProject,pickNewProjectName,pickProject,pickTeam,projectIdFromResolution,requireAuth,requireVercelAuth,requireVercelLogin,resolveProjectByNameOrId,resolveTeam,unresolvedProject,validateTeam,vercelAuthBlockerReason};
@@ -0,0 +1,3 @@
1
+ import type { Prompter } from "./prompter.js";
2
+ /** Runs asynchronous work behind an optional spinner that always stops. */
3
+ export declare function withSpinner<T>(prompter: Prompter, message: string, task: () => Promise<T>): Promise<T>;
@@ -0,0 +1 @@
1
+ async function withSpinner(e,t,n){let r=e.log.spinner?.(t);try{return await n()}finally{r?.stop()}}export{withSpinner};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Removes the npm scope prefix from a package name when present.
3
+ */
4
+ export declare function stripNpmPackageScope(packageName: string): string;
@@ -0,0 +1 @@
1
+ function stripNpmPackageScope(e){let t=e.lastIndexOf(`/`);return t===-1?e:e.slice(t+1)}export{stripNpmPackageScope};
@@ -105,7 +105,7 @@ export default slackChannel({
105
105
 
106
106
  HITL renders as Slack buttons and selects. When the user responds, the parked session (paused awaiting input) resumes.
107
107
 
108
- Authorization prompts are private. A sign-in challenge (OAuth URL, device code) is a credential. Anyone who completes it binds their identity to the session's connection. The default `authorization.required` handler delivers the challenge ephemerally to the triggering user, device code included, and posts a public link-free status only when it has no user to target. The handler receives a private-delivery context with `postEphemeral`, `postDirectMessage` (needs the `im:write` scope), and `state`. There is, intentionally, no public `post` and no raw API access.
108
+ Authorization prompts split public status from private credentials. A sign-in challenge (OAuth URL, device code) is a credential. Anyone who completes it binds their identity to the session's connection. The default `authorization.required` handler posts a public, link-free status in the thread, delivers the actual challenge ephemerally to the triggering user, device code included, and then updates that public status when `authorization.completed` fires. The handler receives a private-delivery context with `postEphemeral`, `postDirectMessage` (needs the `im:write` scope), and `state`. There is, intentionally, no public `post` and no raw API access.
109
109
 
110
110
  ```ts
111
111
  events: {
@@ -124,11 +124,11 @@ import { defineMcpClientConnection } from "eve/connections";
124
124
  export default defineMcpClientConnection({
125
125
  url: "https://mcp.linear.app/sse",
126
126
  description: "Linear workspace: issues, projects, cycles, and comments.",
127
- auth: connect("linear"),
127
+ auth: connect("linear/myagent"),
128
128
  });
129
129
  ```
130
130
 
131
- `"linear"` is the UID you chose when registering the Connect client. Connect-managed OAuth is user-scoped by default, so the runtime resolves the per-user token before each tool call. The full setup (Connect client provisioning, project linking, the runtime consent flow) lives in [Auth & route protection](./guides/auth-and-route-protection).
131
+ `"linear/myagent"` is the UID you chose when registering the Connect client. Connect-managed OAuth is user-scoped by default, so the runtime resolves the per-user token before each tool call. The full setup (Connect client provisioning, project linking, the runtime consent flow) lives in [Auth & route protection](./guides/auth-and-route-protection).
132
132
 
133
133
  ## Self-hosted interactive OAuth
134
134
 
@@ -183,7 +183,7 @@ export default defineMcpClientConnection({
183
183
 
184
184
  Drop `resume` when the provider keeps flow state server-side, so nothing has to cross the step boundary.
185
185
 
186
- `displayName` is presentation-only. The connection's path-derived name still keys the authorization scope, token cache, and callback URL. You can also set `displayName` on the `auth` definition itself (e.g. `auth: { ...connect("sfdc"), displayName: "Salesforce" }`); that definition-level value wins over one the strategy stamps on the challenge, and channels fall back to title-casing the connection name when neither is set.
186
+ `displayName` is presentation-only. The connection's path-derived name still keys the authorization flow, token cache, and callback URL. You can also set `displayName` on the `auth` definition itself (e.g. `auth: { ...connect("salesforce/myagent"), displayName: "Salesforce" }`); that definition-level value wins over one the strategy stamps on the challenge, and channels fall back to title-casing the connection name when neither is set.
187
187
 
188
188
  ### Signaling authorization state
189
189
 
@@ -205,23 +205,26 @@ To narrow a caught error, use `isConnectionAuthorizationRequiredError(err)` and
205
205
 
206
206
  ### Handling a revoked token mid-call
207
207
 
208
- `getToken` only runs _before_ a tool call, so a grant revoked while a tool is mid-flight first surfaces as a downstream `401` inside your `execute`. A plain throw there is only a tool error, so the model sees a failure and the cached bearer sticks around. Instead, map a provider `401` to `ctx.requireAuth()` (or rethrow `ConnectionAuthorizationRequiredError`). eve then evicts the rejected token from its per-step cache and re-runs the consent flow with a fresh one, exactly as it does for a connection whose server rejects the bearer.
208
+ `getToken` only runs _before_ a tool call, so a grant revoked while a tool is mid-flight first surfaces as a downstream `401` inside your `execute`. A plain throw there is only a tool error, so the model sees a failure and the cached bearer sticks around. Instead, map a provider `401` to `ctx.requireAuth(provider)`. eve then evicts the rejected token from its per-step cache and re-runs the consent flow with a fresh one, exactly as it does for a connection whose server rejects the bearer.
209
209
 
210
210
  ```ts title="agent/tools/list_issues.ts"
211
+ import { connect } from "@vercel/connect/eve";
211
212
  import { defineTool } from "eve/tools";
212
213
  import { z } from "zod";
213
214
 
215
+ const linearAuth = connect("linear/myagent");
216
+
214
217
  export default defineTool({
215
218
  description: "List open Linear issues.",
216
219
  inputSchema: z.object({}),
217
220
  async execute(_input, ctx) {
218
- const { token } = await ctx.getToken();
221
+ const { token } = await ctx.getToken(linearAuth);
219
222
  const res = await fetch("https://api.linear.app/graphql", {
220
223
  headers: { authorization: `Bearer ${token}` },
221
224
  });
222
225
  // The grant was revoked since getToken ran: re-challenge instead of
223
226
  // returning a dead-token error to the model.
224
- if (res.status === 401) ctx.requireAuth();
227
+ if (res.status === 401) ctx.requireAuth(linearAuth);
225
228
  return await res.json();
226
229
  },
227
230
  });