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.
- package/CHANGELOG.md +25 -0
- package/dist/src/cli/commands/agent-instructions.d.ts +15 -3
- package/dist/src/cli/commands/agent-instructions.js +3 -1
- package/dist/src/cli/commands/agent-prompt/build-and-verify.md +33 -0
- package/dist/src/cli/commands/agent-prompt/collect-intent.md +18 -0
- package/dist/src/cli/commands/agent-prompt/intro-handoff.md +4 -0
- package/dist/src/cli/commands/agent-prompt/intro-setup.md +5 -0
- package/dist/src/cli/commands/agent-prompt/scaffold.md +19 -0
- package/dist/src/cli/commands/agent-prompt/vercel-connect.md +3 -0
- package/dist/src/cli/commands/deploy.js +1 -1
- package/dist/src/cli/commands/init-repl.d.ts +79 -0
- package/dist/src/cli/commands/init-repl.js +1 -0
- package/dist/src/cli/commands/init.d.ts +6 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/link.d.ts +2 -2
- package/dist/src/cli/commands/link.js +1 -1
- package/dist/src/cli/dev/tui/line-editor.d.ts +2 -0
- package/dist/src/cli/dev/tui/line-editor.js +2 -2
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +26 -7
- package/dist/src/cli/dev/tui/setup-commands.js +2 -2
- package/dist/src/cli/dev/tui/setup-flow.d.ts +10 -3
- package/dist/src/cli/dev/tui/setup-panel.d.ts +16 -8
- package/dist/src/cli/dev/tui/setup-panel.js +3 -3
- package/dist/src/cli/dev/tui/setup-selection-input.d.ts +2 -1
- package/dist/src/cli/dev/tui/setup-selection-input.js +2 -2
- package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
- package/dist/src/cli/dev/tui/terminal-text.d.ts +11 -0
- package/dist/src/cli/dev/tui/terminal-text.js +1 -1
- package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
- package/dist/src/cli/run.d.ts +1 -0
- package/dist/src/cli/run.js +2 -2
- package/dist/src/cli/ui/live-row.js +1 -1
- package/dist/src/cli/ui/progress-pulse.d.ts +10 -0
- package/dist/src/cli/ui/progress-pulse.js +1 -0
- package/dist/src/discover/discover-agent.js +1 -1
- package/dist/src/evals/target.js +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-sdk-types.d.ts +0 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/multiplexed-command.d.ts +22 -0
- package/dist/src/execution/sandbox/multiplexed-command.js +1 -0
- package/dist/src/execution/tool-auth.d.ts +17 -30
- package/dist/src/execution/tool-auth.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-module-loader.js +2 -2
- package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
- package/dist/src/public/channels/github/api.d.ts +5 -0
- package/dist/src/public/channels/github/api.js +1 -1
- package/dist/src/public/channels/github/dispatch.js +1 -1
- package/dist/src/public/channels/github/pr-context.d.ts +10 -0
- package/dist/src/public/channels/github/pr-context.js +2 -2
- package/dist/src/public/channels/slack/connections.d.ts +16 -18
- package/dist/src/public/channels/slack/defaults.d.ts +1 -1
- package/dist/src/public/channels/slack/defaults.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +6 -7
- package/dist/src/public/definitions/tool.d.ts +40 -41
- package/dist/src/public/definitions/tool.js +1 -1
- package/dist/src/public/tools/index.d.ts +1 -1
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/connections/validate-authorization.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/types.d.ts +0 -10
- package/dist/src/setup/ai-gateway-api-key.d.ts +15 -0
- package/dist/src/setup/ai-gateway-api-key.js +1 -0
- package/dist/src/setup/boxes/apply-ai-gateway-credential.js +1 -1
- package/dist/src/setup/boxes/detect-ai-gateway.js +2 -2
- package/dist/src/setup/boxes/link-project.d.ts +8 -14
- package/dist/src/setup/boxes/link-project.js +1 -1
- package/dist/src/setup/boxes/resolve-provisioning.d.ts +2 -1
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/cli/select-component.d.ts +7 -1
- package/dist/src/setup/cli/select-component.js +1 -1
- package/dist/src/setup/cli/select-option-codec.d.ts +1 -0
- package/dist/src/setup/cli/select-option-codec.js +1 -1
- package/dist/src/setup/cli/select-state.d.ts +14 -1
- package/dist/src/setup/cli/select-state.js +1 -1
- package/dist/src/setup/flows/channels.js +1 -1
- package/dist/src/setup/flows/deploy.js +1 -1
- package/dist/src/setup/flows/install-vercel-cli.js +1 -1
- package/dist/src/setup/flows/link.d.ts +2 -1
- package/dist/src/setup/flows/link.js +1 -1
- package/dist/src/setup/flows/login.js +1 -1
- package/dist/src/setup/flows/model.d.ts +5 -4
- package/dist/src/setup/flows/model.js +1 -1
- package/dist/src/setup/flows/vercel.js +2 -2
- package/dist/src/setup/index.d.ts +1 -1
- package/dist/src/setup/package-manager.d.ts +2 -1
- package/dist/src/setup/package-manager.js +1 -1
- package/dist/src/setup/primitives/pm/index.d.ts +1 -1
- package/dist/src/setup/primitives/pm/pnpm.d.ts +11 -1
- package/dist/src/setup/primitives/pm/pnpm.js +5 -2
- package/dist/src/setup/primitives/pm/types.d.ts +10 -1
- package/dist/src/setup/project-resolution.d.ts +9 -6
- package/dist/src/setup/project-resolution.js +1 -1
- package/dist/src/setup/prompter.d.ts +13 -0
- package/dist/src/setup/prompter.js +1 -1
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.d.ts +8 -0
- package/dist/src/setup/scaffold/create/project.js +8 -7
- package/dist/src/setup/scaffold/update/channels.d.ts +8 -0
- package/dist/src/setup/scaffold/update/channels.js +2 -2
- package/dist/src/setup/scaffold/update/package-json.d.ts +3 -0
- package/dist/src/setup/scaffold/update/package-json.js +1 -1
- package/dist/src/setup/scaffold/workspace-glob.d.ts +5 -0
- package/dist/src/setup/scaffold/workspace-glob.js +1 -0
- package/dist/src/setup/scaffold/workspace-root.d.ts +29 -0
- package/dist/src/setup/scaffold/workspace-root.js +1 -0
- package/dist/src/setup/state.d.ts +7 -2
- package/dist/src/setup/vercel-api-failure.d.ts +9 -0
- package/dist/src/setup/vercel-api-failure.js +1 -0
- package/dist/src/setup/vercel-deployment.js +1 -1
- package/dist/src/setup/vercel-project-api.d.ts +25 -0
- package/dist/src/setup/vercel-project-api.js +1 -0
- package/dist/src/setup/vercel-project.d.ts +11 -57
- package/dist/src/setup/vercel-project.js +1 -1
- package/dist/src/setup/with-spinner.d.ts +3 -0
- package/dist/src/setup/with-spinner.js +1 -0
- package/dist/src/shared/package-name.d.ts +4 -0
- package/dist/src/shared/package-name.js +1 -0
- package/docs/channels/slack.mdx +1 -1
- package/docs/connections.mdx +9 -6
- package/docs/getting-started.mdx +8 -3
- package/docs/guides/auth-and-route-protection.md +57 -10
- package/docs/guides/dev-tui.md +3 -3
- package/docs/reference/cli.md +3 -3
- package/docs/reference/typescript-api.md +7 -7
- package/package.json +1 -1
- package/dist/src/cli/commands/init-agent-handoff.md +0 -12
- package/dist/src/cli/commands/init-agent-instructions.md +0 -16
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{
|
|
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
|
|
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()
|
|
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{
|
|
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
|
|
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
|
|
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:
|
|
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{
|
|
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 {
|
|
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
|
-
|
|
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<
|
|
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<
|
|
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.
|
|
131
|
-
*
|
|
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<
|
|
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{
|
|
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 @@
|
|
|
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 @@
|
|
|
1
|
+
function stripNpmPackageScope(e){let t=e.lastIndexOf(`/`);return t===-1?e:e.slice(t+1)}export{stripNpmPackageScope};
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -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
|
|
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: {
|
package/docs/connections.mdx
CHANGED
|
@@ -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
|
|
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()
|
|
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
|
});
|