eve 0.15.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/src/chunks/{use-eve-agent-8X2UMr8q.js → use-eve-agent-BEOUv37s.js} +70 -0
- package/dist/src/chunks/{use-eve-agent-9ZNiSFMb.js → use-eve-agent-C25KOe9i.js} +70 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/dev/tui/agent-header.js +1 -1
- package/dist/src/cli/dev/tui/blocks.js +8 -8
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +10 -3
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +9 -1
- package/dist/src/cli/dev/tui/setup-commands.js +2 -2
- package/dist/src/cli/dev/tui/setup-flow.d.ts +8 -1
- package/dist/src/cli/dev/tui/setup-issues.d.ts +2 -14
- package/dist/src/cli/dev/tui/setup-issues.js +1 -1
- package/dist/src/cli/dev/tui/setup-panel.d.ts +14 -8
- package/dist/src/cli/dev/tui/setup-panel.js +3 -3
- package/dist/src/cli/dev/tui/terminal-renderer.js +6 -6
- package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
- package/dist/src/cli/dev/tui/tui.d.ts +2 -3
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/cli/dev/ui-options.d.ts +21 -0
- package/dist/src/cli/dev/ui-options.js +1 -0
- package/dist/src/cli/run.d.ts +6 -42
- package/dist/src/cli/run.js +2 -2
- package/dist/src/client/authorization-message-parts.d.ts +4 -0
- package/dist/src/client/authorization-message-parts.js +1 -0
- package/dist/src/client/index.d.ts +2 -2
- package/dist/src/client/message-reducer-types.d.ts +40 -1
- package/dist/src/client/message-reducer.d.ts +3 -5
- package/dist/src/client/message-reducer.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +71 -1
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/harness/emission.d.ts +3 -3
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/stream-actions.d.ts +17 -0
- package/dist/src/harness/stream-actions.js +1 -0
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/nitro/host/configure-nitro-routes.js +2 -2
- package/dist/src/internal/nitro/host/dev-authored-source-watcher.d.ts +1 -0
- package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
- package/dist/src/internal/nitro/host/start-development-server.d.ts +8 -0
- package/dist/src/internal/nitro/host/start-development-server.js +2 -2
- package/dist/src/internal/nitro/host.d.ts +1 -1
- package/dist/src/internal/nitro/host.js +1 -1
- package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
- package/dist/src/internal/nitro/routes/dev-runtime-artifacts.d.ts +0 -3
- package/dist/src/internal/nitro/routes/dev-runtime-artifacts.js +1 -1
- package/dist/src/internal/nitro/routes/info.js +1 -1
- package/dist/src/internal/vercel/project-link.d.ts +10 -0
- package/dist/src/internal/vercel/project-link.js +1 -0
- package/dist/src/protocol/message.d.ts +14 -10
- package/dist/src/public/channels/auth.d.ts +26 -13
- package/dist/src/public/channels/auth.js +1 -1
- package/dist/src/react/index.d.ts +1 -1
- package/dist/src/runtime/agent/mock-model-adapter.d.ts +3 -2
- package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
- package/dist/src/runtime/connections/principal.d.ts +2 -0
- package/dist/src/runtime/connections/principal.js +1 -1
- package/dist/src/runtime/connections/types.d.ts +4 -1
- package/dist/src/runtime/framework-channels/index.d.ts +1 -6
- package/dist/src/runtime/framework-channels/index.js +1 -1
- package/dist/src/runtime/governance/auth/oidc.js +1 -1
- package/dist/src/runtime/governance/auth/types.d.ts +14 -10
- package/dist/src/runtime/governance/auth/vercel-oidc-project.d.ts +12 -0
- package/dist/src/runtime/governance/auth/vercel-oidc-project.js +1 -0
- package/dist/src/services/dev-client/client-options.d.ts +5 -0
- package/dist/src/services/dev-client/client-options.js +1 -1
- package/dist/src/services/dev-client/request-headers.d.ts +8 -0
- package/dist/src/services/dev-client/request-headers.js +1 -1
- package/dist/src/services/dev-client/runtime-artifacts.d.ts +1 -0
- package/dist/src/services/dev-client/runtime-artifacts.js +1 -1
- package/dist/src/services/dev-client.d.ts +8 -0
- package/dist/src/services/dev-client.js +1 -1
- package/dist/src/setup/boxes/add-connections.d.ts +10 -8
- package/dist/src/setup/boxes/add-connections.js +1 -1
- package/dist/src/setup/boxes/resolve-provisioning.d.ts +1 -0
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/cli/channel-setup-prompter.d.ts +9 -2
- package/dist/src/setup/cli/channel-setup-prompter.js +1 -1
- package/dist/src/setup/cli/index.d.ts +1 -1
- package/dist/src/setup/cli/prompt-ui.d.ts +2 -0
- package/dist/src/setup/cli/select-state.js +1 -1
- package/dist/src/setup/connection-connector.d.ts +26 -52
- package/dist/src/setup/connection-connector.js +2 -1
- package/dist/src/setup/flows/channels.js +1 -1
- package/dist/src/setup/flows/connections.d.ts +36 -0
- package/dist/src/setup/flows/connections.js +3 -0
- package/dist/src/setup/flows/link.d.ts +1 -0
- package/dist/src/setup/flows/link.js +1 -1
- package/dist/src/setup/project-resolution.d.ts +2 -8
- package/dist/src/setup/project-resolution.js +1 -1
- package/dist/src/setup/prompter.d.ts +2 -0
- package/dist/src/setup/scaffold/connections/catalog.d.ts +10 -5
- package/dist/src/setup/scaffold/connections/catalog.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +11 -11
- package/dist/src/setup/scaffold/create/web-template.d.ts +2 -2
- package/dist/src/setup/scaffold/create/web-template.js +107 -3
- package/dist/src/setup/scaffold/index.d.ts +1 -1
- package/dist/src/setup/scaffold/index.js +1 -1
- package/dist/src/setup/scaffold/update/connections.d.ts +6 -0
- package/dist/src/setup/scaffold/update/connections.js +3 -3
- package/dist/src/svelte/index.d.ts +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/vue/index.d.ts +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/channels/eve.mdx +3 -3
- package/docs/concepts/sessions-runs-and-streaming.md +1 -1
- package/docs/guides/auth-and-route-protection.md +5 -5
- package/docs/guides/dev-tui.md +11 -4
- package/docs/guides/frontend/nextjs.mdx +2 -2
- package/docs/guides/frontend/nuxt.mdx +2 -2
- package/docs/guides/frontend/overview.mdx +34 -4
- package/docs/guides/frontend/sveltekit.mdx +2 -2
- package/docs/reference/cli.md +3 -1
- package/docs/tutorial/ship-it.mdx +1 -1
- package/docs/tutorial/team-playbooks.mdx +1 -1
- package/package.json +2 -2
- package/dist/src/internal/nitro/host/dev-rebuild-registry.d.ts +0 -5
- package/dist/src/internal/nitro/host/dev-rebuild-registry.js +0 -1
|
@@ -1,9 +1,15 @@
|
|
|
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`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.
|
|
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`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.16.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
|
|
6
|
+
`;function packageManagerAiPinTemplateSuffix(e){switch(e){case`bun`:case`npm`:return`,
|
|
7
|
+
"overrides": {
|
|
8
|
+
"ai": "__EVE_INIT_AI_SDK_VERSION__"
|
|
9
|
+
}`;case`yarn`:return`,
|
|
10
|
+
"resolutions": {
|
|
11
|
+
"ai": "__EVE_INIT_AI_SDK_VERSION__"
|
|
12
|
+
}`;case`pnpm`:return``;default:return e}}function packageJsonTemplate(e){return`{
|
|
7
13
|
"name": "__EVE_INIT_APP_NAME__",
|
|
8
14
|
"version": "0.0.0",
|
|
9
15
|
"type": "module",
|
|
@@ -26,15 +32,9 @@ export default defineAgent({
|
|
|
26
32
|
"devDependencies": {
|
|
27
33
|
"@types/node": "__EVE_INIT_TYPES_NODE_VERSION__",
|
|
28
34
|
"typescript": "__EVE_INIT_TYPESCRIPT_VERSION__"
|
|
29
|
-
}${e
|
|
35
|
+
}${e.includeRootOnlyFields?`${packageManagerAiPinTemplateSuffix(e.packageManager)}${ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX}`:``}
|
|
30
36
|
}
|
|
31
37
|
`}const ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX=`,
|
|
32
|
-
"overrides": {
|
|
33
|
-
"ai": "__EVE_INIT_AI_SDK_VERSION__"
|
|
34
|
-
},
|
|
35
|
-
"resolutions": {
|
|
36
|
-
"ai": "__EVE_INIT_AI_SDK_VERSION__"
|
|
37
|
-
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": "__EVE_INIT_NODE_ENGINE__"
|
|
40
40
|
}
|
|
@@ -81,7 +81,7 @@ from the installed eve package docs. In most installs, those docs are at
|
|
|
81
81
|
installed \`eve\` package location first and read its \`docs/\` directory. If
|
|
82
82
|
package docs are unavailable, use https://eve.dev/docs as a fallback.
|
|
83
83
|
`,"CLAUDE.md":`@AGENTS.md
|
|
84
|
-
`};function templateFiles(e
|
|
84
|
+
`};function templateFiles(e){return{"agent/agent.ts":e.byokProvider?`import { defineAgent } from "eve";
|
|
85
85
|
|
|
86
86
|
export default defineAgent({
|
|
87
87
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -95,4 +95,4 @@ export default defineAgent({
|
|
|
95
95
|
},
|
|
96
96
|
},
|
|
97
97
|
});
|
|
98
|
-
`:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(
|
|
98
|
+
`:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate({includeRootOnlyFields:e.includeRootOnlyPackageJsonFields,packageManager:e.packageManager})}}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`),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({byokProvider:u,includeRootOnlyPackageJsonFields:!h,packageManager:d}))){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,7 +1,7 @@
|
|
|
1
1
|
export declare const WEB_APP_TEMPLATE_FILES: {
|
|
2
|
-
readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n //
|
|
2
|
+
readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
|
|
3
3
|
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n PromptInput,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\ntype AgentStatus = ReturnType<typeof useEveAgent>["status"];\n\nexport function AgentChat() {\n const agent = useEveAgent();\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isEmpty = agent.data.messages.length === 0;\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if (!text || isBusy) return;\n\n await agent.send({ message: text });\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea placeholder="Send a message…" />\n <PromptInputSubmit onStop={agent.stop} status={agent.status} />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {isEmpty ? null : (\n <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">\n <span className="flex min-w-0 items-center gap-2">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n <StatusDot status={agent.status} />\n </span>\n </header>\n )}\n\n {agent.error ? (\n <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">\n <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{agent.error.message}</p>\n </div>\n </div>\n </div>\n ) : null}\n\n {isEmpty ? null : (\n <Conversation className="min-h-0 flex-1">\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">\n {agent.data.messages.map((message, index) => (\n <AgentMessage\n canRespond={!isBusy}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => agent.send({ inputResponses })}\n />\n ))}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n )}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n isEmpty\n ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"\n : "max-w-3xl shrink-0 pb-6",\n )}\n >\n {isEmpty ? (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n ) : null}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction StatusDot({ status }: { readonly status: AgentStatus }) {\n const isLive = status === "submitted" || status === "streaming";\n const tone =\n status === "error"\n ? "bg-destructive"\n : isLive\n ? "bg-emerald-500"\n : status === "ready"\n ? "bg-muted-foreground"\n : "bg-muted-foreground/50";\n\n return (\n <span className="relative flex size-1">\n {isLive ? (\n <span\n className={cn(\n "absolute inline-flex size-full animate-ping rounded-full opacity-75",\n tone,\n )}\n />\n ) : null}\n <span className={cn("relative inline-flex size-1 rounded-full transition-colors", tone)} />\n </span>\n );\n}\n';
|
|
4
|
-
readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {
|
|
4
|
+
readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n EveAuthorizationPart,\n EveDynamicToolPart,\n EveMessage,\n EveMessagePart,\n} from "eve/react";\nimport { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } from "lucide-react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n\n return (\n <Message\n data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n from={message.role}\n >\n <MessageContent>\n {message.parts.map((part, index) => (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ))}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n showCaret,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly showCaret: boolean;\n}) {\n switch (part.type) {\n case "step-start":\n return null;\n case "text":\n return (\n <MessageResponse caret="block" isAnimating={showCaret}>\n {part.text}\n </MessageResponse>\n );\n case "reasoning":\n return (\n <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n <ReasoningTrigger />\n <ReasoningContent>{part.text}</ReasoningContent>\n </Reasoning>\n );\n case "authorization":\n return <AuthorizationPrompt part={part} />;\n case "dynamic-tool":\n return (\n <Tool\n defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n >\n <ToolHeader\n state={part.state}\n title={part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n <ToolInput input={part.input} />\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </ToolContent>\n </Tool>\n );\n }\n}\n\nfunction AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {\n const isAuthorized = part.state === "completed" && part.outcome === "authorized";\n const isCompleted = part.state === "completed";\n const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;\n const instructions = part.authorization?.instructions;\n const shouldShowInstructions = instructions !== undefined && instructions !== part.description;\n\n return (\n <div\n className={cn(\n "space-y-3 rounded-md border p-3",\n isAuthorized\n ? "border-emerald-500/30 bg-emerald-500/5"\n : isCompleted\n ? "border-destructive/30 bg-destructive/5"\n : "border-blue-500/30 bg-blue-500/5",\n )}\n >\n <div className="flex items-start gap-3">\n <span\n className={cn(\n "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",\n isAuthorized\n ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"\n : isCompleted\n ? "bg-destructive/10 text-destructive"\n : "bg-blue-500/10 text-blue-700 dark:text-blue-300",\n )}\n >\n <Icon className="size-4" />\n </span>\n <div className="min-w-0 flex-1 space-y-2">\n <p className="font-medium text-sm">{authorizationTitle(part)}</p>\n <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>\n {shouldShowInstructions ? (\n <p className="text-muted-foreground text-sm">{instructions}</p>\n ) : null}\n {part.state === "required" && part.authorization?.userCode ? (\n <div className="flex flex-wrap items-center gap-2 text-sm">\n <span className="text-muted-foreground">Code</span>\n <code className="rounded-md bg-background px-2 py-1 font-mono">\n {part.authorization.userCode}\n </code>\n </div>\n ) : null}\n {part.state === "required" && part.authorization?.url ? (\n <Button asChild size="sm">\n <a href={part.authorization.url} rel="noreferrer" target="_blank">\n <ExternalLinkIcon className="size-4" />\n Sign in with {part.displayName}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n\nfunction authorizationTitle(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return `Connect ${part.displayName}`;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected`;\n }\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}`;\n}\n\nfunction authorizationDescription(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return part.description;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected.`;\n }\n const tail = part.reason !== undefined ? ` (${part.reason})` : "";\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}${tail}.`;\n}\n\nfunction formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {\n switch (outcome) {\n case "authorized":\n return "authorized";\n case "declined":\n return "declined";\n case "failed":\n return "failed";\n case "timed-out":\n return "timed out";\n }\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveDynamicToolPart;\n}) {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (!inputRequest) {\n return null;\n }\n\n const inputResponse = part.toolMetadata?.eve?.inputResponse;\n const selectedOption = inputRequest.options?.find(\n (option) => option.id === inputResponse?.optionId,\n );\n\n return (\n <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n {inputResponse ? (\n <p className="font-medium text-sm">\n Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond}\n key={option.id}\n onClick={() => {\n void onInputResponses([\n {\n optionId: option.id,\n requestId: inputRequest.requestId,\n },\n ]);\n }}\n size="sm"\n type="button"\n variant={option.style === "danger" ? "destructive" : "default"}\n >\n {option.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n switch (part.type) {\n case "authorization":\n return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;\n case "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
|
|
5
5
|
readonly "app/globals.css": '@import "tailwindcss";\n@source "../node_modules/streamdown/dist/*.js";\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;\n --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;\n}\n\n:root {\n color-scheme: light;\n /* Soft neutral page with white elevated surfaces so cards/composer pop. */\n --background: oklch(0.971 0 0);\n --foreground: oklch(0.16 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.16 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.16 0 0);\n --primary: oklch(0.19 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.94 0 0);\n --secondary-foreground: oklch(0.19 0 0);\n --muted: oklch(0.94 0 0);\n --muted-foreground: oklch(0.6 0 0);\n --accent: oklch(0.94 0 0);\n --accent-foreground: oklch(0.19 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.916 0 0);\n --input: oklch(0.916 0 0);\n --ring: oklch(0.708 0 0);\n --radius: 0.625rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n }\n}\n\n* {\n border-color: var(--border);\n}\n\nhtml {\n height: 100%;\n}\n\nbody {\n min-height: 100%;\n margin: 0;\n background: var(--background);\n font-family: var(--font-sans);\n}\n\nbutton,\ninput,\ntextarea {\n font: inherit;\n}\n';
|
|
6
6
|
readonly "app/layout.tsx": 'import type { Metadata } from "next";\nimport { Geist, Geist_Mono } from "next/font/google";\nimport type { ReactNode } from "react";\nimport { TooltipProvider } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport "./globals.css";\n\nconst sans = Geist({\n variable: "--font-sans",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nconst mono = Geist_Mono({\n variable: "--font-mono",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nexport const metadata: Metadata = {\n title: "__EVE_INIT_APP_NAME__",\n description: "A Next.js starter for eve agents with AI Elements.",\n};\n\nexport default function RootLayout({ children }: { readonly children: ReactNode }) {\n return (\n <html className={cn(sans.variable, mono.variable)} lang="en">\n <body>\n <TooltipProvider>{children}</TooltipProvider>\n </body>\n </html>\n );\n}\n';
|
|
7
7
|
readonly "app/page.tsx": 'import { AgentChat } from "@/app/_components/agent-chat";\n\nexport default function Page() {\n return <AgentChat />;\n}\n';
|
|
@@ -3,10 +3,10 @@ import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";
|
|
|
3
3
|
|
|
4
4
|
export default eveChannel({
|
|
5
5
|
auth: [
|
|
6
|
-
// Open on localhost for \`eve dev\` and the REPL; ignored in production.
|
|
7
|
-
localDev(),
|
|
8
6
|
// Lets the eve TUI and your Vercel deployments reach the deployed agent.
|
|
9
7
|
vercelOidc(),
|
|
8
|
+
// Open on localhost for \`eve dev\` and the REPL; ignored in production.
|
|
9
|
+
localDev(),
|
|
10
10
|
// This placeholder will not allow browser requests in production.
|
|
11
11
|
// Replace it with your app's auth provider, like Auth.js or Clerk,
|
|
12
12
|
// or use none() for a public demo.
|
|
@@ -142,7 +142,13 @@ function StatusDot({ status }: { readonly status: AgentStatus }) {
|
|
|
142
142
|
}
|
|
143
143
|
`,"app/_components/agent-message.tsx":`"use client";
|
|
144
144
|
|
|
145
|
-
import type {
|
|
145
|
+
import type {
|
|
146
|
+
EveAuthorizationPart,
|
|
147
|
+
EveDynamicToolPart,
|
|
148
|
+
EveMessage,
|
|
149
|
+
EveMessagePart,
|
|
150
|
+
} from "eve/react";
|
|
151
|
+
import { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } from "lucide-react";
|
|
146
152
|
import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";
|
|
147
153
|
import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";
|
|
148
154
|
import {
|
|
@@ -153,6 +159,7 @@ import {
|
|
|
153
159
|
ToolOutput,
|
|
154
160
|
} from "@/components/ai-elements/tool";
|
|
155
161
|
import { Button } from "@/components/ui/button";
|
|
162
|
+
import { cn } from "@/lib/utils";
|
|
156
163
|
|
|
157
164
|
export type AgentInputResponse = {
|
|
158
165
|
readonly optionId?: string;
|
|
@@ -223,6 +230,8 @@ function AgentMessagePart({
|
|
|
223
230
|
<ReasoningContent>{part.text}</ReasoningContent>
|
|
224
231
|
</Reasoning>
|
|
225
232
|
);
|
|
233
|
+
case "authorization":
|
|
234
|
+
return <AuthorizationPrompt part={part} />;
|
|
226
235
|
case "dynamic-tool":
|
|
227
236
|
return (
|
|
228
237
|
<Tool
|
|
@@ -248,6 +257,99 @@ function AgentMessagePart({
|
|
|
248
257
|
}
|
|
249
258
|
}
|
|
250
259
|
|
|
260
|
+
function AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {
|
|
261
|
+
const isAuthorized = part.state === "completed" && part.outcome === "authorized";
|
|
262
|
+
const isCompleted = part.state === "completed";
|
|
263
|
+
const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;
|
|
264
|
+
const instructions = part.authorization?.instructions;
|
|
265
|
+
const shouldShowInstructions = instructions !== undefined && instructions !== part.description;
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<div
|
|
269
|
+
className={cn(
|
|
270
|
+
"space-y-3 rounded-md border p-3",
|
|
271
|
+
isAuthorized
|
|
272
|
+
? "border-emerald-500/30 bg-emerald-500/5"
|
|
273
|
+
: isCompleted
|
|
274
|
+
? "border-destructive/30 bg-destructive/5"
|
|
275
|
+
: "border-blue-500/30 bg-blue-500/5",
|
|
276
|
+
)}
|
|
277
|
+
>
|
|
278
|
+
<div className="flex items-start gap-3">
|
|
279
|
+
<span
|
|
280
|
+
className={cn(
|
|
281
|
+
"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",
|
|
282
|
+
isAuthorized
|
|
283
|
+
? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
|
|
284
|
+
: isCompleted
|
|
285
|
+
? "bg-destructive/10 text-destructive"
|
|
286
|
+
: "bg-blue-500/10 text-blue-700 dark:text-blue-300",
|
|
287
|
+
)}
|
|
288
|
+
>
|
|
289
|
+
<Icon className="size-4" />
|
|
290
|
+
</span>
|
|
291
|
+
<div className="min-w-0 flex-1 space-y-2">
|
|
292
|
+
<p className="font-medium text-sm">{authorizationTitle(part)}</p>
|
|
293
|
+
<p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>
|
|
294
|
+
{shouldShowInstructions ? (
|
|
295
|
+
<p className="text-muted-foreground text-sm">{instructions}</p>
|
|
296
|
+
) : null}
|
|
297
|
+
{part.state === "required" && part.authorization?.userCode ? (
|
|
298
|
+
<div className="flex flex-wrap items-center gap-2 text-sm">
|
|
299
|
+
<span className="text-muted-foreground">Code</span>
|
|
300
|
+
<code className="rounded-md bg-background px-2 py-1 font-mono">
|
|
301
|
+
{part.authorization.userCode}
|
|
302
|
+
</code>
|
|
303
|
+
</div>
|
|
304
|
+
) : null}
|
|
305
|
+
{part.state === "required" && part.authorization?.url ? (
|
|
306
|
+
<Button asChild size="sm">
|
|
307
|
+
<a href={part.authorization.url} rel="noreferrer" target="_blank">
|
|
308
|
+
<ExternalLinkIcon className="size-4" />
|
|
309
|
+
Sign in with {part.displayName}
|
|
310
|
+
</a>
|
|
311
|
+
</Button>
|
|
312
|
+
) : null}
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function authorizationTitle(part: EveAuthorizationPart): string {
|
|
320
|
+
if (part.state === "required") {
|
|
321
|
+
return \`Connect \${part.displayName}\`;
|
|
322
|
+
}
|
|
323
|
+
if (part.outcome === "authorized") {
|
|
324
|
+
return \`\${part.displayName} connected\`;
|
|
325
|
+
}
|
|
326
|
+
return \`\${part.displayName} authorization \${formatAuthorizationOutcome(part.outcome)}\`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function authorizationDescription(part: EveAuthorizationPart): string {
|
|
330
|
+
if (part.state === "required") {
|
|
331
|
+
return part.description;
|
|
332
|
+
}
|
|
333
|
+
if (part.outcome === "authorized") {
|
|
334
|
+
return \`\${part.displayName} connected.\`;
|
|
335
|
+
}
|
|
336
|
+
const tail = part.reason !== undefined ? \` (\${part.reason})\` : "";
|
|
337
|
+
return \`\${part.displayName} authorization \${formatAuthorizationOutcome(part.outcome)}\${tail}.\`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {
|
|
341
|
+
switch (outcome) {
|
|
342
|
+
case "authorized":
|
|
343
|
+
return "authorized";
|
|
344
|
+
case "declined":
|
|
345
|
+
return "declined";
|
|
346
|
+
case "failed":
|
|
347
|
+
return "failed";
|
|
348
|
+
case "timed-out":
|
|
349
|
+
return "timed out";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
251
353
|
function InputRequestActions({
|
|
252
354
|
canRespond,
|
|
253
355
|
onInputResponses,
|
|
@@ -303,6 +405,8 @@ function InputRequestActions({
|
|
|
303
405
|
|
|
304
406
|
function partKey(part: EveMessagePart, index: number): string {
|
|
305
407
|
switch (part.type) {
|
|
408
|
+
case "authorization":
|
|
409
|
+
return \`authorization:\${part.turnId}:\${part.stepIndex}:\${part.name}\`;
|
|
306
410
|
case "dynamic-tool":
|
|
307
411
|
return part.toolCallId;
|
|
308
412
|
default:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { byokProviderEnvVar, CURRENT_DIRECTORY_PROJECT_NAME, isEveProject, scaffoldBaseProject, type EvePackageContract, type ScaffoldBaseProjectOptions, } from "./create/project.js";
|
|
2
2
|
export { DEFAULT_SLACK_CONNECTOR_SLUG, SLACK_CHANNEL_DEFAULT_ROUTE, deriveSlackConnectorSlug, ensureChannel, hasVercelHostFramework, isNextJsProject, listAuthoredChannels, normalizeSlackConnectorSlug, type ChannelKind, type ChannelMutationResult, type EnsureChannelOptions, type SlackConnectorSlug, type WebPackageVersions, } from "./update/channels.js";
|
|
3
3
|
export { SCAFFOLDABLE_CHANNELS, type ScaffoldableChannel } from "./channels-catalog.js";
|
|
4
|
-
export { ensureConnection, listAuthoredConnections, type ConnectionInput, type ConnectionMutationAction, type ConnectionMutationResult, type EnsureConnectionOptions, } from "./update/connections.js";
|
|
4
|
+
export { ensureConnection, ensureConnectionDependencies, listAuthoredConnections, type ConnectionInput, type ConnectionMutationAction, type ConnectionMutationResult, type EnsureConnectionOptions, type EnsureConnectionDependenciesOptions, } from "./update/connections.js";
|
|
5
5
|
export { catalogSlugs, CONNECTION_CATALOG, CUSTOM_CONNECTION_SLUG, effectiveProtocols, endpointForProtocol, getCatalogEntry, isValidConnectionSlug, SUPPORTED_PROTOCOLS, type ConnectionAuthSpec, type ConnectionCatalogEntry, type ConnectionProtocol, type CustomConnectionInput, type EnvHeader, type McpEndpoint, type OpenApiEndpoint, } from "./connections/catalog.js";
|
|
6
6
|
export { WriteFileExistsError } from "./files.js";
|
|
7
7
|
export { HumanActionRequiredError, type HumanAction } from "../human-action.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,catalogSlugs,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug}from"./connections/catalog.js";import{HumanActionRequiredError}from"../human-action.js";import{SCAFFOLDABLE_CHANNELS}from"./channels-catalog.js";import{WriteFileExistsError}from"./files.js";import{CURRENT_DIRECTORY_PROJECT_NAME,byokProviderEnvVar,isEveProject,scaffoldBaseProject}from"./create/project.js";import{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug}from"./update/channels.js";import{ensureConnection,ensureConnectionDependencies,listAuthoredConnections}from"./update/connections.js";export{CONNECTION_CATALOG,CURRENT_DIRECTORY_PROJECT_NAME,CUSTOM_CONNECTION_SLUG,DEFAULT_SLACK_CONNECTOR_SLUG,HumanActionRequiredError,SCAFFOLDABLE_CHANNELS,SLACK_CHANNEL_DEFAULT_ROUTE,SUPPORTED_PROTOCOLS,WriteFileExistsError,byokProviderEnvVar,catalogSlugs,deriveSlackConnectorSlug,effectiveProtocols,endpointForProtocol,ensureChannel,ensureConnection,ensureConnectionDependencies,getCatalogEntry,hasVercelHostFramework,isEveProject,isNextJsProject,isValidConnectionSlug,listAuthoredChannels,listAuthoredConnections,normalizeSlackConnectorSlug,scaffoldBaseProject};
|
|
@@ -28,6 +28,12 @@ export interface EnsureConnectionOptions {
|
|
|
28
28
|
force?: boolean;
|
|
29
29
|
connectPackageVersion?: string;
|
|
30
30
|
}
|
|
31
|
+
export interface EnsureConnectionDependenciesOptions {
|
|
32
|
+
projectRoot: string;
|
|
33
|
+
connectPackageVersion?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Adds the package dependency required by a Connect-auth connection. */
|
|
36
|
+
export declare function ensureConnectionDependencies(options: EnsureConnectionDependenciesOptions): Promise<PackageJsonMutation[]>;
|
|
31
37
|
/**
|
|
32
38
|
* Scaffolds `agent/connections/<slug>.ts` from a catalog entry or custom
|
|
33
39
|
* input, patching `package.json` for Connect-auth connections and seeding
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{endpointForProtocol}from"../connections/catalog.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{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
|
|
1
|
+
import{endpointForProtocol}from"../connections/catalog.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{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
|
|
2
2
|
`)}\n }),\n`;case`none`:return``}}function renderMcpTemplate(e,t,n){return`${n.kind===`connect`?`import { connect } from "@vercel/connect/eve";
|
|
3
3
|
import { defineMcpClientConnection } from "eve/connections";
|
|
4
4
|
`:`import { defineMcpClientConnection } from "eve/connections";
|
|
@@ -15,7 +15,7 @@ export default defineOpenAPIConnection({
|
|
|
15
15
|
spec: "${e.spec}",
|
|
16
16
|
${i} description: "${t}",
|
|
17
17
|
${authBlock(n)}});
|
|
18
|
-
`}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
|
|
18
|
+
`}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}async function ensureConnectionDependencies(e){let t=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);return ensureConnectDependency(join(e.projectRoot,`package.json`),t)}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
|
|
19
19
|
`)?`
|
|
20
20
|
`:``}${i.map(e=>`${e}=`).join(`
|
|
21
|
-
`)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion
|
|
21
|
+
`)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);d.push(...await ensureConnectDependency(join(r.projectRoot,`package.json`),e))}await writeTextFile(c,renderTemplate(r.protocol,s,r.entry.description,o),{force:!0});let f=await seedEnvPlaceholders(join(r.projectRoot,`.env.local`),l),p={slug:a,protocol:r.protocol,action:u?`overwritten`:`created`,filePath:c,filesWritten:[c],filesSkipped:[],packageJsonUpdated:d,envKeysAdded:f,envKeysRequired:l};return u&&(p.filesOverwritten=[c]),p}async function listAuthoredConnections(e){let t=join(e,USER_AUTHORED_CONNECTION_DIR),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{ensureConnection,ensureConnectionDependencies,listAuthoredConnections};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { useEveAgent, type PrepareSend, type UseEveAgentOptions, type UseEveAgentReturn, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#svelte/use-eve-agent.js";
|
|
2
2
|
export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
|
|
3
|
-
export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
|
|
3
|
+
export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
|
package/dist/src/svelte/index.js
CHANGED
package/dist/src/vue/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { useEveAgent, type PrepareSend, type UseEveAgentOptions, type UseEveAgentReturn, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#vue/use-eve-agent.js";
|
|
2
2
|
export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
|
|
3
|
-
export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
|
|
3
|
+
export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
|
package/dist/src/vue/index.js
CHANGED
package/docs/channels/eve.mdx
CHANGED
|
@@ -12,7 +12,7 @@ import { eveChannel } from "eve/channels/eve";
|
|
|
12
12
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
13
13
|
|
|
14
14
|
export default eveChannel({
|
|
15
|
-
auth: [
|
|
15
|
+
auth: [vercelOidc(), localDev()],
|
|
16
16
|
});
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -54,7 +54,7 @@ The `auth` option decides who can call these routes. The built-in helpers cover
|
|
|
54
54
|
|
|
55
55
|
Neither admits browser users or external clients in production. For a public app, wire the channel to your own auth (Clerk, Auth.js, your own OIDC/JWT verification, an API-key verifier, or any custom `AuthFn`). Vercel OIDC is optional; use it only when Vercel-issued deployment tokens are part of your trust model.
|
|
56
56
|
|
|
57
|
-
`eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel
|
|
57
|
+
`eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel checks Vercel OIDC before falling back to localhost access, and includes `placeholderAuth()`, which returns a setup-focused 401 in production until you swap it for real auth. Delete the file and eve falls back to `[vercelOidc(), localDev()]`, which still does not admit browser users in production.
|
|
58
58
|
|
|
59
59
|
For the full auth model and helper list, see [Auth & route protection](../guides/auth-and-route-protection).
|
|
60
60
|
|
|
@@ -67,7 +67,7 @@ import { eveChannel, defaultEveAuth } from "eve/channels/eve";
|
|
|
67
67
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
68
68
|
|
|
69
69
|
export default eveChannel({
|
|
70
|
-
auth: [
|
|
70
|
+
auth: [vercelOidc(), localDev()],
|
|
71
71
|
onMessage(ctx, message) {
|
|
72
72
|
const callerId = ctx.eve.caller?.principalId ?? "anonymous";
|
|
73
73
|
return {
|
|
@@ -108,7 +108,7 @@ Start with the [TypeScript SDK](../guides/client/overview) guide. It covers basi
|
|
|
108
108
|
curl http://127.0.0.1:3000/eve/v1/info
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
The route uses the same default auth chain as the eve channel (`[
|
|
111
|
+
The route uses the same default auth chain as the eve channel (`[vercelOidc(), localDev()]`). A local Vercel OIDC bearer takes precedence; other local requests fall back to development access. A deployed Vercel target requires a valid OIDC bearer, with a same-project bypass for in-deployment callers. See [auth & route protection](../guides/auth-and-route-protection).
|
|
112
112
|
|
|
113
113
|
## Dispatch order
|
|
114
114
|
|
|
@@ -27,7 +27,7 @@ import { eveChannel } from "eve/channels/eve";
|
|
|
27
27
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
28
28
|
|
|
29
29
|
export default eveChannel({
|
|
30
|
-
auth: [
|
|
30
|
+
auth: [vercelOidc(), localDev()],
|
|
31
31
|
});
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -62,11 +62,11 @@ function appSession(): AuthFn<Request> {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export default eveChannel({
|
|
65
|
-
auth: [appSession(),
|
|
65
|
+
auth: [appSession(), vercelOidc(), localDev()],
|
|
66
66
|
});
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
Put your own providers ahead of the catch-all helpers. Any entry that doesn't recognize the caller returns `null`, and the walk moves on. On non-Vercel hosts, omit `vercelOidc()` unless you specifically want to accept Vercel-issued tokens.
|
|
69
|
+
Put your own providers ahead of the catch-all helpers. `localDev()` is the final fallback: put `vercelOidc()` before it so a local Vercel OIDC bearer can resolve a user instead of being shadowed by the synthetic local principal. Any entry that doesn't recognize the caller returns `null`, and the walk moves on. On non-Vercel hosts, omit `vercelOidc()` unless you specifically want to accept Vercel-issued tokens.
|
|
70
70
|
|
|
71
71
|
To reject with a precise status instead of skipping, throw:
|
|
72
72
|
|
|
@@ -197,11 +197,11 @@ import { eveChannel } from "eve/channels/eve";
|
|
|
197
197
|
import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";
|
|
198
198
|
|
|
199
199
|
export default eveChannel({
|
|
200
|
-
auth: [
|
|
200
|
+
auth: [vercelOidc(), localDev(), placeholderAuth()],
|
|
201
201
|
});
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
In production, `placeholderAuth()` returns a structured `401` so a generated web chat app can say "auth isn't configured yet" instead of throwing an internal error. Replace it before a browser caller submits a production request: swap in your app's `AuthFn` or one of the shipped helpers. Delete the authored channel file entirely and eve falls back to the framework default `[
|
|
204
|
+
In production, `placeholderAuth()` returns a structured `401` so a generated web chat app can say "auth isn't configured yet" instead of throwing an internal error. Replace it before a browser caller submits a production request: swap in your app's `AuthFn` or one of the shipped helpers. Delete the authored channel file entirely and eve falls back to the framework default `[vercelOidc(), localDev()]`, which also rejects production browser traffic.
|
|
205
205
|
|
|
206
206
|
You do not have to keep `vercelOidc()` in the final policy. For a self-hosted app, an app-embedded frontend, or any deployment that uses a non-Vercel identity system, use `httpBasic()`, `jwtHmac()`, `jwtEcdsa()`, generic `oidc()`, or a custom `AuthFn` that maps your verified user/session/API key into a `SessionAuthContext`.
|
|
207
207
|
|
package/docs/guides/dev-tui.md
CHANGED
|
@@ -16,7 +16,7 @@ On startup the TUI prints a brand line with your agent's name, plus a rotating t
|
|
|
16
16
|
Use /channels to add more ways to reach your agent.
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
If agent discovery reported problems, an error and warning count renders between the two lines. Instructions, tools, skills, and subagents are one `eve info` away, and `/help` lists every command. The TUI also runs a startup check.
|
|
19
|
+
If agent discovery reported problems, an error and warning count renders between the two lines. Instructions, tools, skills, and subagents are one `eve info` away, and `/help` lists every command. The TUI also runs a startup check. A fresh `eve init` starts local `eve dev` with `/model` prefilled. That input starts onboarding: the flow installs the Vercel CLI if needed, asks you to log in if needed, then opens `/model` before the first prompt. Other `eve dev` sessions show missing setup as an attention line, with each command's outcome hanging under it on a `⎿` connector.
|
|
20
20
|
|
|
21
21
|
## Reading the transcript
|
|
22
22
|
|
|
@@ -30,12 +30,13 @@ Errors render compactly with docs links highlighted. A code bug escaping your ag
|
|
|
30
30
|
|
|
31
31
|
## Slash commands
|
|
32
32
|
|
|
33
|
-
Each command echoes as an invocation line, asks through a bordered panel that takes the input area's place (one question at a time, separate from the chat transcript), and finishes with a one-line `⎿` result. Loading states stay on the ephemeral status line instead of piling into the transcript; model, channel, and the Vercel CLI commands (`/vc:install`, `/vc:login`) use the same green square pulse as the build phase, while `/deploy` keeps a spinner. Setup menus render the selected option with a filled arrow and an inverse label padded by one space on each side. Text prompts use a blinking block cursor over the character at the caret. The selected label is blue normally and yellow for warning rows.
|
|
33
|
+
Each command echoes as an invocation line, asks through a bordered panel that takes the input area's place (one question at a time, separate from the chat transcript), and finishes with a one-line `⎿` result. Loading states stay on the ephemeral status line instead of piling into the transcript; model, channel, connection, and the Vercel CLI commands (`/vc:install`, `/vc:login`) use the same green square pulse as the build phase, while `/deploy` keeps a spinner. A connection setup waiting for browser action changes that pulse and the word "browser" to yellow. Setup menus render the selected option with a filled arrow and an inverse label padded by one space on each side. Text prompts use a blinking block cursor over the character at the caret. The selected label is blue normally and yellow for warning rows.
|
|
34
34
|
|
|
35
35
|
| Command | Does |
|
|
36
36
|
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
37
37
|
| `/model` | Opens a configure menu that loops until Done (or Esc). See [Configure the model and provider](#configure-the-model-and-provider). |
|
|
38
38
|
| `/channels` | Shows the agent's channel list and adds the one you pick. See [Add a channel](#add-a-channel). |
|
|
39
|
+
| `/connect` | Shows the Vercel Connect MCP catalog and configures the server you pick. See [Add a connection](#add-a-connection). |
|
|
39
40
|
| `/deploy` | Ships the agent to Vercel production, linking the directory first when it is unlinked. |
|
|
40
41
|
| `/vc:install` | Installs the Vercel CLI. Available locally and on a remote session. |
|
|
41
42
|
| `/vc:login` | Logs in to Vercel locally. On a remote session, resolves the deployment's project, refreshes its OIDC token, and confirms any required Trusted Sources rule. |
|
|
@@ -44,7 +45,7 @@ Each command echoes as an invocation line, asks through a bordered panel that ta
|
|
|
44
45
|
| `/exit` | Quits the TUI. |
|
|
45
46
|
| `/help` | Lists the commands available for the current local or remote session. |
|
|
46
47
|
|
|
47
|
-
`/model`, `/channels`, and `/deploy` manage the project
|
|
48
|
+
`/model`, `/channels`, `/connect`, and `/deploy` manage the local agent or its linked project. They are available only when `eve dev` runs the server locally, not when connected to a remote server with `--url`.
|
|
48
49
|
|
|
49
50
|
### Configure the model and provider
|
|
50
51
|
|
|
@@ -58,6 +59,12 @@ The provider row demands attention (a bold yellow "Configure model access" with
|
|
|
58
59
|
|
|
59
60
|
`/channels` shows the agent's channel list. Already-registered channels render as checked, focusable rows with an "Already installed" hint. Picking one adds it (including the Slack Connect provisioning), then installs the dependencies the scaffold added so the dev server can load the new channels right away. After each addition the list repaints with the channel checked, until Done (or Esc) leaves the flow.
|
|
60
61
|
|
|
62
|
+
### Add a connection
|
|
63
|
+
|
|
64
|
+
`/connect` shows a searchable list of MCP servers available through Vercel Connect. Already-authored connections remain checked. Logged-out users are directed to `/vc:login`. When the directory is not linked, selecting a server opens the same team and project flow used by `/model`, including creating a project or linking an existing one.
|
|
65
|
+
|
|
66
|
+
For a selected server, eve first tries to attach the provider's canonical connector. If that fails, choose an existing connector from a searchable list or create one with a specific name. The fallback stays scoped to the team selected by the linked project. A connector created by the current attempt is removed if attachment or connection-file patching fails. Successful setup writes `agent/connections/<name>.ts`, records the attached connector UID, installs the new dependency so the dev server can load it, then returns to the main prompt.
|
|
67
|
+
|
|
61
68
|
## Keyboard shortcuts
|
|
62
69
|
|
|
63
70
|
Chat and freeform `ask_question` inputs behave like a shell line editor.
|
|
@@ -83,7 +90,7 @@ When the agent needs something from you, the TUI asks inline.
|
|
|
83
90
|
|
|
84
91
|
- Tool approvals are a `y` or `n`.
|
|
85
92
|
- Option questions let you pick with `↑` / `↓` and `Enter`, or you can compose a multi-line freeform answer.
|
|
86
|
-
- If a tool needs an authorized [connection](../connections), the URL shows up right in the transcript, and the turn picks back up once you finish the flow.
|
|
93
|
+
- If a tool needs an authorized [connection](../connections), the URL shows up right in the transcript, and the turn picks back up once you finish the flow. The same local `eve dev` server owns the callback route, so keep that command running until the browser returns. `eve dev --url` connects to an already-running server and does not start a local callback host.
|
|
87
94
|
|
|
88
95
|
## Control what logs show
|
|
89
96
|
|
|
@@ -61,13 +61,13 @@ const agent = useEveAgent({
|
|
|
61
61
|
});
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [
|
|
64
|
+
The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [vercelOidc(), localDev()] })`: `vercelOidc()` gets the first chance to resolve a Vercel caller, `localDev()` opens the remaining localhost requests, and everything else gets a `401`. To run your app's own auth policy, add `agent/channels/eve.ts`:
|
|
65
65
|
|
|
66
66
|
```ts title="agent/channels/eve.ts"
|
|
67
67
|
import { eveChannel } from "eve/channels/eve";
|
|
68
68
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
69
69
|
|
|
70
|
-
export default eveChannel({ auth: [
|
|
70
|
+
export default eveChannel({ auth: [vercelOidc(), localDev()] });
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
For a public demo, use `none()` (also from `eve/channels/auth`) to skip authentication. See [Channels](../../channels/overview) and [Auth & route protection](../auth-and-route-protection).
|
|
@@ -60,13 +60,13 @@ async function handleSubmit() {
|
|
|
60
60
|
</template>
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [
|
|
63
|
+
The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [vercelOidc(), localDev()] })`: `vercelOidc()` gets the first chance to resolve a Vercel caller, `localDev()` opens the remaining localhost requests, and everything else gets a `401`. To run your own auth policy, add `agent/channels/eve.ts`:
|
|
64
64
|
|
|
65
65
|
```ts title="agent/channels/eve.ts"
|
|
66
66
|
import { eveChannel } from "eve/channels/eve";
|
|
67
67
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
68
68
|
|
|
69
|
-
export default eveChannel({ auth: [
|
|
69
|
+
export default eveChannel({ auth: [vercelOidc(), localDev()] });
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
For a public demo, use `none()` (also from `eve/channels/auth`) to skip authentication. See [Channels](../../channels/overview) and [Auth & route protection](../auth-and-route-protection).
|