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,5 +1,5 @@
|
|
|
1
|
-
import{getGitHubPullRequest,listGitHubPullRequestFiles}from"#public/channels/github/api.js";const MAX_PATCH_BYTES=2e4,GITHUB_DEFAULT_EXCLUDED_DIFF_FILES=[`**/pnpm-lock.yaml`,`**/package-lock.json`,`**/npm-shrinkwrap.json`,`**/yarn.lock`,`**/bun.lockb`,`**/Cargo.lock`,`**/go.sum`,`**/poetry.lock`,`**/Pipfile.lock`,`**/uv.lock`,`**/composer.lock`,`**/Gemfile.lock`,`**/*.min.js`,`**/*.min.css`,`**/*.map`,`**/*.snap`];async function buildGitHubPullRequestContext(
|
|
1
|
+
import{getGitHubPullRequest,listGitHubCompareFiles,listGitHubPullRequestFiles}from"#public/channels/github/api.js";const MAX_PATCH_BYTES=2e4,GITHUB_DEFAULT_EXCLUDED_DIFF_FILES=[`**/pnpm-lock.yaml`,`**/package-lock.json`,`**/npm-shrinkwrap.json`,`**/yarn.lock`,`**/bun.lockb`,`**/Cargo.lock`,`**/go.sum`,`**/poetry.lock`,`**/Pipfile.lock`,`**/uv.lock`,`**/composer.lock`,`**/Gemfile.lock`,`**/*.min.js`,`**/*.min.css`,`**/*.map`,`**/*.snap`];async function buildGitHubPullRequestContext(r){if(r.pullRequestNumber===null)return;let a=withPinnedPullRequestShas(await getGitHubPullRequest({api:r.api,credentials:r.credentials,installationId:r.installationId,owner:r.owner,pullRequestNumber:r.pullRequestNumber,repo:r.repo}),r),o=renderPullRequestMetadata(a),s=readNonEmptySha(r.baseSha)??readNonEmptySha(a.base.sha),c=readNonEmptySha(r.headSha)??readNonEmptySha(a.head.sha),l=readNonEmptySha(r.headSha)!==void 0&&c!==void 0&&s!==void 0?(await listGitHubCompareFiles({api:r.api,baseSha:s,credentials:r.credentials,headSha:c,installationId:r.installationId,owner:r.owner,repo:r.repo})).slice(0,50):await listGitHubPullRequestFiles({api:r.api,credentials:r.credentials,installationId:r.installationId,owner:r.owner,perPage:50,pullRequestNumber:r.pullRequestNumber,repo:r.repo}),u=[...GITHUB_DEFAULT_EXCLUDED_DIFF_FILES,...r.config?.excludedFiles??[]];return o.push(``,...renderPullRequestFiles(l,u)),[[`GitHub pull request context for the current turn. `,`Use this as background for the user's GitHub comment.`,``,o.join(`
|
|
2
2
|
`)].join(`
|
|
3
|
-
`)]}function mergeGitHubContext(e){let t=e.github??[],n=e.hook??[];if(!(t.length===0&&n.length===0))return[...t,...n]}function renderPullRequestMetadata(e){return[`<github_pull_request>`,`number: ${e.number}`,`title: ${e.title}`,`state: ${e.state??`unknown`}`,`draft: ${e.draft?`true`:`false`}`,...e.author?.login?[`author: ${e.author.login}`]:[],...e.htmlUrl?[`url: ${e.htmlUrl}`]:[],...e.base.ref?[`base_ref: ${e.base.ref}`]:[],...e.base.sha?[`base_sha: ${e.base.sha}`]:[],...e.head.ref?[`head_ref: ${e.head.ref}`]:[],...e.head.sha?[`head_sha: ${e.head.sha}`]:[],...e.mergeable===null||e.mergeable===void 0?[]:[`mergeable: ${e.mergeable?`true`:`false`}`],...e.changedFiles===void 0?[]:[`changed_files: ${e.changedFiles}`],...e.additions===void 0?[]:[`additions: ${e.additions}`],...e.deletions===void 0?[]:[`deletions: ${e.deletions}`],...e.body?[``,`body:`,indent(truncateText(e.body,4e3))]:[],`</github_pull_request>`]}function renderPullRequestFiles(e,t){if(e.length===0)return[`<github_pull_request_files>`,`none`,`</github_pull_request_files>`];let
|
|
3
|
+
`)]}function mergeGitHubContext(e){let t=e.github??[],n=e.hook??[];if(!(t.length===0&&n.length===0))return[...t,...n]}function withPinnedPullRequestShas(e,t){let n=readNonEmptySha(t.headSha),r=readNonEmptySha(t.baseSha);return n===void 0&&r===void 0?e:{...e,base:{...e.base,sha:r??e.base.sha},head:{...e.head,sha:n??e.head.sha}}}function readNonEmptySha(e){return typeof e==`string`&&e.trim().length>0?e.trim():void 0}function renderPullRequestMetadata(e){return[`<github_pull_request>`,`number: ${e.number}`,`title: ${e.title}`,`state: ${e.state??`unknown`}`,`draft: ${e.draft?`true`:`false`}`,...e.author?.login?[`author: ${e.author.login}`]:[],...e.htmlUrl?[`url: ${e.htmlUrl}`]:[],...e.base.ref?[`base_ref: ${e.base.ref}`]:[],...e.base.sha?[`base_sha: ${e.base.sha}`]:[],...e.head.ref?[`head_ref: ${e.head.ref}`]:[],...e.head.sha?[`head_sha: ${e.head.sha}`]:[],...e.mergeable===null||e.mergeable===void 0?[]:[`mergeable: ${e.mergeable?`true`:`false`}`],...e.changedFiles===void 0?[]:[`changed_files: ${e.changedFiles}`],...e.additions===void 0?[]:[`additions: ${e.additions}`],...e.deletions===void 0?[]:[`deletions: ${e.deletions}`],...e.body?[``,`body:`,indent(truncateText(e.body,4e3))]:[],`</github_pull_request>`]}function renderPullRequestFiles(e,t){if(e.length===0)return[`<github_pull_request_files>`,`none`,`</github_pull_request_files>`];let n=[`<github_pull_request_files>`],i=MAX_PATCH_BYTES,a=!1;for(let r of e){if(n.push(renderFileSummary(r)),fileMatchesAnyGlob(r.filename,t)){n.push(` patch omitted (excluded)`);continue}if(r.patch===void 0)continue;if(i<=0){a=!0;continue}let e=truncateByBytes(r.patch,i);n.push(`patch:`,indent(e.text)),i-=e.bytes,e.truncated&&(a=!0,i=0)}return a&&n.push(``,`patches_truncated: true`,`max_patch_bytes: ${MAX_PATCH_BYTES}`),n.push(`</github_pull_request_files>`),n}function renderFileSummary(e){let t=[e.status??`modified`,e.additions===void 0?void 0:`+${e.additions}`,e.deletions===void 0?void 0:`-${e.deletions}`].filter(e=>e!==void 0);return`- ${e.filename}${t.length>0?` (${t.join(`, `)})`:``}`}function fileMatchesAnyGlob(e,t){return t.some(t=>globToRegExp(t).test(e))}function globToRegExp(e){let t=``;for(let n=0;n<e.length;n+=1){let r=e.charAt(n);r===`*`?e[n+1]===`*`?(t+=`.*`,n+=1,e[n+1]===`/`&&(n+=1)):t+=`[^/]*`:`\\^$.|?+()[]{}`.includes(r)?t+=`\\${r}`:t+=r}return RegExp(`^${t}$`,`u`)}function truncateByBytes(e,t){let n=new TextEncoder().encode(e);return n.byteLength<=t?{bytes:n.byteLength,text:e,truncated:!1}:{bytes:t,text:`${new TextDecoder().decode(n.slice(0,t))}\n[truncated]`,truncated:!0}}function truncateText(e,t){return e.length<=t?e:`${e.slice(0,t)}\n[truncated]`}function indent(e){return e.split(`
|
|
4
4
|
`).map(e=>` ${e}`).join(`
|
|
5
5
|
`)}export{GITHUB_DEFAULT_EXCLUDED_DIFF_FILES,buildGitHubPullRequestContext,fileMatchesAnyGlob,mergeGitHubContext};
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
* an OAuth-style authorization flow (e.g. signing in to Linear). The
|
|
6
6
|
* challenge is a credential: anyone in a shared thread could complete a
|
|
7
7
|
* posted sign-in link and bind their own identity to the session. The
|
|
8
|
-
* default handler therefore
|
|
9
|
-
*
|
|
8
|
+
* default handler therefore posts a link-free public status while
|
|
9
|
+
* delivering the actual challenge as an ephemeral "Sign in with X"
|
|
10
|
+
* message visible only to the triggering user.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
* URL, or the ephemeral delivery fails)
|
|
13
|
-
*
|
|
14
|
-
* `authorization.completed` handler edits that
|
|
12
|
+
* When no user can be targeted (no triggering user id, no challenge
|
|
13
|
+
* URL, or the ephemeral delivery fails), the public status still leaves
|
|
14
|
+
* the shared thread with safe progress feedback. The matching
|
|
15
|
+
* `authorization.completed` handler edits that status post in place to
|
|
15
16
|
* surface the outcome (`authorized` / `declined` / `failed` /
|
|
16
|
-
* `timed-out`)
|
|
17
|
-
* and the outcome surfaces through the resumed turn itself.
|
|
17
|
+
* `timed-out`).
|
|
18
18
|
*/
|
|
19
19
|
import type { ConnectionAuthorizationOutcome } from "#protocol/message.js";
|
|
20
20
|
export type { ConnectionAuthorizationOutcome };
|
|
@@ -25,22 +25,20 @@ export type { ConnectionAuthorizationOutcome };
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function formatConnectionDisplayName(connectionName: string): string;
|
|
27
27
|
/**
|
|
28
|
-
* Public
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* there's no one to act on it.
|
|
28
|
+
* Public status text for an authorization challenge. Deliberately
|
|
29
|
+
* link-free: it must stay safe to post in a shared thread. When the
|
|
30
|
+
* channel cannot identify a triggering user (rare — schedule-initiated
|
|
31
|
+
* sessions or events that lack actor metadata) the text drops the
|
|
32
|
+
* "Connect with" call-to-action since there's no one to act on it.
|
|
34
33
|
*/
|
|
35
34
|
export declare function buildAuthRequiredPublicText(input: {
|
|
36
35
|
readonly displayName: string;
|
|
37
36
|
readonly hasUser: boolean;
|
|
38
37
|
}): string;
|
|
39
38
|
/**
|
|
40
|
-
* Final-state markdown for the public
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* status message.
|
|
39
|
+
* Final-state markdown for the public status message. Edited in place by
|
|
40
|
+
* `authorization.completed` so the thread sees resolution without
|
|
41
|
+
* scrolling.
|
|
44
42
|
*/
|
|
45
43
|
export declare function buildAuthCompletedText(input: {
|
|
46
44
|
readonly displayName: string;
|
|
@@ -33,7 +33,7 @@ export declare function defaultInputRequestedHandler(): NonNullable<SlackChannel
|
|
|
33
33
|
* and the connection-authorization status flow. Each is overridable
|
|
34
34
|
* per-event by passing the same key under `slackChannel({ events })`.
|
|
35
35
|
* Typed as the internal full-context map because the default
|
|
36
|
-
* `authorization.required` handler owns the public link-free
|
|
36
|
+
* `authorization.required` handler owns the public link-free status,
|
|
37
37
|
* which user overrides cannot express.
|
|
38
38
|
*/
|
|
39
39
|
export declare const defaultEvents: SlackChannelInternalEvents;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{truncateMessageText,truncateTypingStatus}from"#public/channels/slack/limits.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{buildAuthCompletedText,buildAuthEphemeralBlocks,buildAuthRequiredPublicText,formatConnectionDisplayName}from"#public/channels/slack/connections.js";import{renderInputRequestBlocks}from"#public/channels/slack/hitl.js";const log=createLogger(`slack.defaults`);function defaultSlackAuth(e,t){let n=e.author;return n?buildSlackAuthContext({channelId:t.slack.channelId,fullName:n.fullName,isBot:n.isBot,teamId:e.teamId,threadTs:t.slack.threadTs,userId:n.userId,userName:n.userName}):null}async function defaultOnAppMention(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}async function defaultOnDirectMessage(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function defaultInputRequestedHandler(){return async(e,t,n)=>{if(e.requests.length===0)return;let i=truncateMessageText(e.requests.map(e=>e.prompt).join(`
|
|
2
2
|
`));await t.thread.post({blocks:e.requests.flatMap(renderInputRequestBlocks),text:i})}}const defaultEvents={async"turn.started"(e,t,n){t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null,await t.thread.startTyping(`Working...`)},async"reasoning.appended"(e,t,n){let r=firstNonEmptyLine(e.reasoningSoFar);if(r===void 0)return;let a=truncateTypingStatus(r),o=t.state.lastReasoningTypingStatus,s=o!=null&&a.startsWith(o)&&a.length>=o.length+4,c=Date.now(),l=t.state.lastReasoningTypingAtMs;if(!s&&l!=null){let e=c-l;if(e>=0&&e<5e3)return}await t.thread.startTyping(a),t.state.lastReasoningTypingAtMs=c,t.state.lastReasoningTypingStatus=a},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,r){await t.thread.startTyping(truncateTypingStatus(r));return}let a=e.actions.map(e=>e.kind===`tool-call`?e.toolName:e.kind);await t.thread.startTyping(truncateTypingStatus(`Running ${a.join(`, `)}...`))},async"message.completed"(e,t,n){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null;return}t.state.pendingToolCallMessage=null,e.message&&await t.thread.post(e.message)},async"turn.failed"(e,r,i){let a=formatErrorHint(e),o=extractErrorId(e.details);await r.thread.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...o?[``,`_Error id: \`${o}\`_`]:[]].join(`
|
|
3
3
|
`))},async"session.failed"(e,r){let i=formatErrorHint(e),a=extractErrorId(e.details);await r.thread.post([`This session couldn't recover from an error${i}.`,``,`Start a new thread to continue — I can't pick this one back up.`,...a?[``,`_Error id: \`${a}\`_`]:[]].join(`
|
|
4
|
-
`))},async"authorization.required"(e,t,n){let r=e.authorization?.displayName??formatConnectionDisplayName(e.name),i=t.state.triggeringUserId??null,a=e.authorization?.url;if(i&&a){let n=e.authorization?.userCode;try{await t.thread.postEphemeral(i,{blocks:buildAuthEphemeralBlocks({displayName:r,url:a,userCode:n}),text:n?`Sign in with ${r}: ${a} (code: ${n})`:`Sign in with ${r}: ${a}`})
|
|
4
|
+
`))},async"authorization.required"(e,t,n){let r=e.authorization?.displayName??formatConnectionDisplayName(e.name),i=t.state.triggeringUserId??null,a=e.authorization?.url,o=t.state.pendingAuthMessageTs??{};if(o[e.name]===void 0){let n=buildAuthRequiredPublicText({displayName:r,hasUser:i!==null});try{let r=await t.thread.post(n);r.id&&(t.state.pendingAuthMessageTs={...o,[e.name]:r.id})}catch(t){log.error(`Slack auth public message delivery failed`,{name:e.name,error:t})}}if(i&&a){let n=e.authorization?.userCode;try{await t.thread.postEphemeral(i,{blocks:buildAuthEphemeralBlocks({displayName:r,url:a,userCode:n}),text:n?`Sign in with ${r}: ${a} (code: ${n})`:`Sign in with ${r}: ${a}`})}catch(t){log.error(`Slack auth ephemeral delivery failed`,{name:e.name,error:t})}}},async"authorization.completed"(e,t,n){let r=t.state.pendingAuthMessageTs??{},i=r[e.name];if(i===void 0)return;let a=buildAuthCompletedText({displayName:e.authorization?.displayName??formatConnectionDisplayName(e.name),outcome:e.outcome,reason:e.reason});try{await t.slack.request(`chat.update`,{channel:t.slack.channelId,ts:i,text:a})}catch(t){log.error(`Slack auth status edit failed`,{name:e.name,error:t})}let s={...r};delete s[e.name],t.state.pendingAuthMessageTs=s}};export{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,defaultSlackAuth};
|
|
@@ -116,11 +116,10 @@ export interface SlackChannelState {
|
|
|
116
116
|
lastReasoningTypingStatus?: string | null;
|
|
117
117
|
/**
|
|
118
118
|
* Connection name to Slack message ts. Each entry is the public
|
|
119
|
-
* link-free
|
|
120
|
-
* `authorization.required` handler
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* normal ephemeral path stores nothing here.
|
|
119
|
+
* link-free status post created by the default
|
|
120
|
+
* `authorization.required` handler; the matching
|
|
121
|
+
* `authorization.completed` handler edits it in place to surface the
|
|
122
|
+
* resolution outcome.
|
|
124
123
|
*/
|
|
125
124
|
pendingAuthMessageTs?: Record<string, string>;
|
|
126
125
|
}
|
|
@@ -272,8 +271,8 @@ export interface SlackChannelEvents {
|
|
|
272
271
|
* Full-context variant of {@link SlackChannelEvents} consumed by the
|
|
273
272
|
* channel internals. The framework's default `authorization.required`
|
|
274
273
|
* handler keeps the full {@link SlackEventContext} because it owns the
|
|
275
|
-
* public link-free
|
|
276
|
-
*
|
|
274
|
+
* public link-free status while user overrides remain private-only. The
|
|
275
|
+
* factory adapts user overrides into this shape with
|
|
277
276
|
* {@link constrainAuthorizationRequired}.
|
|
278
277
|
*/
|
|
279
278
|
export interface SlackChannelInternalEvents extends Omit<SlackChannelEvents, "authorization.required"> {
|
|
@@ -2,7 +2,7 @@ import type { StandardJSONSchemaV1 } from "#compiled/@standard-schema/spec/index
|
|
|
2
2
|
import type { PublicToolDefinition, ToolModelOutput } from "#shared/tool-definition.js";
|
|
3
3
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
4
4
|
import type { JsonObject } from "#shared/json.js";
|
|
5
|
-
import type { AuthorizationDefinition, NonInteractiveAuthorizationDefinition, TokenResult } from "#runtime/connections/types.js";
|
|
5
|
+
import type { AuthorizationDefinition, ConnectionAuthorizationContext, NonInteractiveAuthorizationDefinition, TokenResult } from "#runtime/connections/types.js";
|
|
6
6
|
import { type DynamicEvents, type DynamicSentinel } from "#shared/dynamic-tool-definition.js";
|
|
7
7
|
type ApprovalToolInput<TInput> = TInput extends object ? Readonly<TInput> : TInput;
|
|
8
8
|
type ApprovalContextInput<TInput> = unknown extends TInput ? Record<string, unknown> : TInput;
|
|
@@ -21,45 +21,62 @@ export interface NeedsApprovalContext<TInput = Record<string, unknown>> {
|
|
|
21
21
|
}
|
|
22
22
|
export type { ToolModelOutput } from "#shared/tool-definition.js";
|
|
23
23
|
/**
|
|
24
|
-
* Authorization
|
|
25
|
-
*
|
|
24
|
+
* Authorization provider passed to {@link ToolContext.getToken} or
|
|
25
|
+
* {@link ToolContext.requireAuth}. Accepts the same shapes as a connection's
|
|
26
|
+
* `auth`:
|
|
26
27
|
* - a `getToken`-only object (static API keys, pre-provisioned JWTs);
|
|
27
28
|
* `principalType` may be omitted and defaults to `"app"`.
|
|
28
|
-
* - a full interactive OAuth definition (e.g. `connect("okta")` from
|
|
29
|
+
* - a full interactive OAuth definition (e.g. `connect("okta/myagent")` from
|
|
29
30
|
* `@vercel/connect/eve`, or {@link defineInteractiveAuthorization}).
|
|
30
31
|
*/
|
|
31
32
|
export type ToolAuthDefinition = (Omit<NonInteractiveAuthorizationDefinition, "principalType"> & {
|
|
32
33
|
readonly principalType?: NonInteractiveAuthorizationDefinition["principalType"];
|
|
33
34
|
}) | AuthorizationDefinition;
|
|
35
|
+
export type ToolAuthProvider = ToolAuthDefinition;
|
|
36
|
+
/**
|
|
37
|
+
* Controls Eve runtime behavior for an inline tool auth provider.
|
|
38
|
+
*/
|
|
39
|
+
export interface ToolAuthOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Connection metadata passed through to provider callbacks. Tool-only
|
|
42
|
+
* providers usually leave this unset; connection-backed helpers can use it
|
|
43
|
+
* to receive the upstream server URL.
|
|
44
|
+
*/
|
|
45
|
+
readonly connection?: ConnectionAuthorizationContext;
|
|
46
|
+
/**
|
|
47
|
+
* Optional human-readable provider name shown in sign-in UI. Presentation
|
|
48
|
+
* only; it does not affect OAuth scopes, token cache keys, or callback URLs.
|
|
49
|
+
*/
|
|
50
|
+
readonly displayName?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Optional Eve auth-flow key for token caches, callback URLs, pending
|
|
53
|
+
* authorization state, and authorization completion. This is not an OAuth
|
|
54
|
+
* scope. For Vercel Connect OAuth targeting such as `scopes`, `resources`,
|
|
55
|
+
* or `authorizationDetails`, configure the provider with
|
|
56
|
+
* `connect({ connector, tokenParams })`.
|
|
57
|
+
*/
|
|
58
|
+
readonly authKey?: string;
|
|
59
|
+
}
|
|
34
60
|
/**
|
|
35
61
|
* Authored tool context. Passed as the last argument to
|
|
36
62
|
* {@link ToolDefinition.execute}.
|
|
37
63
|
*
|
|
38
|
-
* Extends {@link SessionContext} with token accessors.
|
|
39
|
-
*
|
|
40
|
-
* calling them on a tool without `auth` throws.
|
|
64
|
+
* Extends {@link SessionContext} with token accessors. Passing a provider
|
|
65
|
+
* resolves that provider inline, which lets one tool use multiple credentials.
|
|
41
66
|
*/
|
|
42
67
|
export type ToolContext = SessionContext & {
|
|
43
68
|
/**
|
|
44
|
-
* Resolves the bearer token for
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* `ConnectionAuthorizationRequiredError`; the runtime catches it,
|
|
48
|
-
* suspends the turn on a framework-owned callback URL, shows a
|
|
49
|
-
* "Sign in" affordance, and re-runs the tool after the OAuth
|
|
50
|
-
* callback completes.
|
|
51
|
-
*
|
|
52
|
-
* Throws when the tool does not declare an `auth` strategy.
|
|
69
|
+
* Resolves the bearer token for an inline provider. This accepts the same
|
|
70
|
+
* auth shapes as a connection's `auth` field, including `connect("...")`
|
|
71
|
+
* from `@vercel/connect/eve`.
|
|
53
72
|
*/
|
|
54
|
-
getToken(): Promise<TokenResult>;
|
|
73
|
+
getToken(provider: ToolAuthProvider, options?: ToolAuthOptions): Promise<TokenResult>;
|
|
55
74
|
/**
|
|
56
|
-
* Signals that the caller must complete
|
|
57
|
-
* before proceeding.
|
|
58
|
-
*
|
|
59
|
-
* and re-runs the tool after sign-in. Use it to gate a tool on
|
|
60
|
-
* authorization without resolving a token first.
|
|
75
|
+
* Signals that the caller must complete authorization for an inline
|
|
76
|
+
* provider before proceeding. Use this after a downstream `401` rejects a
|
|
77
|
+
* token returned by {@link getToken}.
|
|
61
78
|
*/
|
|
62
|
-
requireAuth(): never;
|
|
79
|
+
requireAuth(provider: ToolAuthProvider, options?: ToolAuthOptions): never;
|
|
63
80
|
};
|
|
64
81
|
/**
|
|
65
82
|
* Public tool definition authored in `agent/tools/*.ts`.
|
|
@@ -70,20 +87,6 @@ export type ToolContext = SessionContext & {
|
|
|
70
87
|
*/
|
|
71
88
|
export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefinition<TInput, TOutput> & {
|
|
72
89
|
execute(input: TInput, ctx: ToolContext): Promise<TOutput> | TOutput;
|
|
73
|
-
/**
|
|
74
|
-
* Optional authorization strategy. When set, the execute context gains a
|
|
75
|
-
* working {@link ToolContext.getToken} / {@link ToolContext.requireAuth},
|
|
76
|
-
* and a thrown `ConnectionAuthorizationRequiredError` (implicit from
|
|
77
|
-
* `getToken()` or explicit via `requireAuth()`) drives the framework's
|
|
78
|
-
* interactive consent flow, the same machinery MCP connections use, scoped
|
|
79
|
-
* to this tool's name.
|
|
80
|
-
*
|
|
81
|
-
* Use `connect("...")` from `@vercel/connect/eve` for Vercel
|
|
82
|
-
* Connect-backed OAuth, {@link defineInteractiveAuthorization} for a
|
|
83
|
-
* custom interactive flow, or a plain `{ getToken }` object for
|
|
84
|
-
* static/pre-provisioned credentials.
|
|
85
|
-
*/
|
|
86
|
-
auth?: ToolAuthDefinition;
|
|
87
90
|
/**
|
|
88
91
|
* Optional per-tool approval gate. The return value determines whether
|
|
89
92
|
* user approval is required before executing this tool.
|
|
@@ -120,7 +123,6 @@ export declare function defineTool<TInputSchema extends StandardJSONSchemaV1<unk
|
|
|
120
123
|
execute(input: StandardJSONSchemaV1.InferOutput<TInputSchema>, ctx: ToolContext): Promise<StandardJSONSchemaV1.InferOutput<TOutputSchema>> | StandardJSONSchemaV1.InferOutput<TOutputSchema>;
|
|
121
124
|
needsApproval?: ToolDefinition<StandardJSONSchemaV1.InferOutput<TInputSchema>, unknown>["needsApproval"];
|
|
122
125
|
toModelOutput?: ToolDefinition<unknown, StandardJSONSchemaV1.InferOutput<TOutputSchema>>["toModelOutput"];
|
|
123
|
-
auth?: ToolAuthDefinition;
|
|
124
126
|
}): ToolDefinition<StandardJSONSchemaV1.InferOutput<TInputSchema>, StandardJSONSchemaV1.InferOutput<TOutputSchema>>;
|
|
125
127
|
export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown, unknown>, TOutput>(definition: {
|
|
126
128
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -129,7 +131,6 @@ export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown,
|
|
|
129
131
|
execute(input: StandardJSONSchemaV1.InferOutput<TSchema>, ctx: ToolContext): Promise<TOutput> | TOutput;
|
|
130
132
|
needsApproval?: ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, unknown>["needsApproval"];
|
|
131
133
|
toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
|
|
132
|
-
auth?: ToolAuthDefinition;
|
|
133
134
|
}): ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, TOutput>;
|
|
134
135
|
export declare function defineTool<TOutputSchema extends StandardJSONSchemaV1<unknown, unknown>>(definition: {
|
|
135
136
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -138,7 +139,6 @@ export declare function defineTool<TOutputSchema extends StandardJSONSchemaV1<un
|
|
|
138
139
|
execute(input: Record<string, unknown>, ctx: ToolContext): Promise<StandardJSONSchemaV1.InferOutput<TOutputSchema>> | StandardJSONSchemaV1.InferOutput<TOutputSchema>;
|
|
139
140
|
needsApproval?: ToolDefinition<Record<string, unknown>, unknown>["needsApproval"];
|
|
140
141
|
toModelOutput?: ToolDefinition<unknown, StandardJSONSchemaV1.InferOutput<TOutputSchema>>["toModelOutput"];
|
|
141
|
-
auth?: ToolAuthDefinition;
|
|
142
142
|
}): ToolDefinition<Record<string, unknown>, StandardJSONSchemaV1.InferOutput<TOutputSchema>>;
|
|
143
143
|
export declare function defineTool<TOutput>(definition: {
|
|
144
144
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -147,7 +147,6 @@ export declare function defineTool<TOutput>(definition: {
|
|
|
147
147
|
execute(input: Record<string, unknown>, ctx: ToolContext): Promise<TOutput> | TOutput;
|
|
148
148
|
needsApproval?: ToolDefinition<Record<string, unknown>, unknown>["needsApproval"];
|
|
149
149
|
toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
|
|
150
|
-
auth?: ToolAuthDefinition;
|
|
151
150
|
}): ToolDefinition<Record<string, unknown>, TOutput>;
|
|
152
151
|
export declare function defineTool<TInput = unknown, TOutput = unknown>(definition: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
153
152
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{
|
|
1
|
+
import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{stampDefinitionKey}from"#public/tool-result-narrowing.js";function defineTool(e){if(e.auth!==void 0)throw Error(`defineTool: The "auth" field is no longer supported. Pass auth providers inline to ctx.getToken(provider) or ctx.requireAuth(provider).`);return Object.assign(e,{[TOOL_BRAND]:!0}),stampDefinitionKey(e,`tool:${e.description}`),e}function defineDynamic(t){let n={kind:DYNAMIC_SENTINEL_KIND,events:t.events};return stampDefinitionKey(n,`dynamic:${Object.keys(t.events).join(`,`)}`),n}const DISABLED_TOOL_SENTINEL_KIND=`eve:disabled-tool`;function disableTool(){return{kind:DISABLED_TOOL_SENTINEL_KIND}}function isDisabledToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_TOOL_SENTINEL_KIND}const ENABLE_WORKFLOW_TOOL_SENTINEL_KIND=`eve:enable-workflow-tool`,ExperimentalWorkflow=Object.freeze({kind:ENABLE_WORKFLOW_TOOL_SENTINEL_KIND});function isEnableWorkflowToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===ENABLE_WORKFLOW_TOOL_SENTINEL_KIND}export{ExperimentalWorkflow,defineDynamic,defineTool,disableTool,isDisabledToolSentinel,isEnableWorkflowToolSentinel};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool authoring helpers for `agent/tools/*.ts` files.
|
|
3
3
|
*/
|
|
4
|
-
export { type DisabledToolSentinel, type EnableWorkflowToolSentinel, defineDynamic, defineTool, disableTool, isDisabledToolSentinel, isEnableWorkflowToolSentinel, type NeedsApprovalContext, type ToolDefinition, type ToolContext, type ToolModelOutput, ExperimentalWorkflow, } from "#public/definitions/tool.js";
|
|
4
|
+
export { type DisabledToolSentinel, type EnableWorkflowToolSentinel, defineDynamic, defineTool, disableTool, isDisabledToolSentinel, isEnableWorkflowToolSentinel, type NeedsApprovalContext, type ToolAuthOptions, type ToolAuthProvider, type ToolDefinition, type ToolContext, type ToolModelOutput, ExperimentalWorkflow, } from "#public/definitions/tool.js";
|
|
5
5
|
export type { DynamicToolEntry, DynamicEvents, DynamicToolEvents, DynamicResolveContext, DynamicSentinel, DynamicToolSet, DynamicToolResult, } from "#shared/dynamic-tool-definition.js";
|
|
6
6
|
export { type SessionContext } from "#public/definitions/callback-context.js";
|
|
7
7
|
export { toolResultFrom, type MatchedConnectionResult, type MatchedToolResult, type ToolResultFromFn, } from "#public/tool-result-narrowing.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contextStorage,loadContext}from"#context/container.js";import{
|
|
1
|
+
import{contextStorage,loadContext}from"#context/container.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{evictCachedToken,readCachedToken,writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";async function resolveScopedToken(t){let{scope:n,authorization:r,connection:i}=t,a=contextStorage.getStore(),o=resolveConnectionPrincipal(n,r,a);if(a===void 0)return await r.getToken({connection:i,principal:o});let u=principalKey(o),d=readCachedToken(a,n,u);if(d!==void 0)return d;let f=await r.getToken({connection:i,principal:o});return writeCachedToken(a,n,u,f),f}async function evictScopedToken(t){let{scope:n,authorization:r,connection:i}=t,a=contextStorage.getStore();if(a===void 0)return;let s;try{s=resolveConnectionPrincipal(n,r,a),evictCachedToken(a,n,principalKey(s))}catch{return}try{await r.evict?.({connection:i,principal:s})}catch{}}async function completeScopedAuthorization(e){let{scope:r,authorization:i,connection:o}=e;if(!supportsInteractiveAuthorization(i))return!1;let s=getAuthorizationResult(r);if(s===void 0)return!1;let u=i,d=loadContext(),f=resolveConnectionPrincipal(r,u,d),p=await u.completeAuthorization({callbackUrl:s.hookUrl,connection:o,principal:f,resume:s.resume,callback:s.callback});return writeCachedToken(d,r,principalKey(f),p),!0}async function startScopedAuthorization(e){let{scope:t,authorization:n,connection:o}=e;if(!supportsInteractiveAuthorization(n))return;let s=getHookUrl(t);if(s===void 0)return;let c=n,l=resolveConnectionPrincipal(t,c),{challenge:u,resume:d}=await c.startAuthorization({callbackUrl:s,connection:o,principal:l});return requestAuthorization([{challenge:stampChallengeDisplayName(u,n),hookUrl:s,name:t,resume:d}])}function stampChallengeDisplayName(e,t){let n=t.displayName??e.displayName;return n===e.displayName?e:{...e,displayName:n}}export{completeScopedAuthorization,evictScopedToken,resolveScopedToken,stampChallengeDisplayName,startScopedAuthorization};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function validateAuthorizationSpec(e,t=`auth`){if(typeof e!=`object`||!e)return`The "${t}" field must be an object with a "getToken" method.`;let n=e;if(typeof n.getToken!=`function`)return`The "${t}.getToken" field must be a function returning Promise<{ token }>.`;let r=n.startAuthorization!==void 0,i=n.completeAuthorization!==void 0;if(!r&&!i&&n.principalType!==void 0&&n.principalType!==`app`&&n.principalType!==`user`)return`The "${t}.principalType" field must be "app" or "user".`;if(r!==i)return`The "${t}" field must provide either both "startAuthorization" and "completeAuthorization" (interactive OAuth) or neither (getToken-only). Got only "${r?`startAuthorization`:`completeAuthorization`}".`;if(r&&typeof n.startAuthorization!=`function`)return`The "${t}.startAuthorization" field must be a function when provided.`;if(i&&typeof n.completeAuthorization!=`function`)return`The "${t}.completeAuthorization" field must be a function when provided.`;if(r&&n.principalType!==`user`)return`Interactive authorization (startAuthorization + completeAuthorization) is restricted to "principalType": "user" in v1. App-level credentials must use a getToken-only definition.`;if(n.displayName!==void 0&&(typeof n.displayName!=`string`||n.displayName.length===0))return`The "${t}.displayName" field must be a non-empty string when provided.`}function normalizeAuthorizationSpec(e,t,n=`auth`){let r=validateAuthorizationSpec(e,n);if(r!==void 0)throw Error(`${t} ${r}`);let i=e,a=extractVercelConnectMarker(i.vercelConnect),o=i.displayName;if(i.startAuthorization!==void 0&&i.completeAuthorization!==void 0){let e={completeAuthorization:i.completeAuthorization,getToken:i.getToken,principalType:`user`,startAuthorization:i.startAuthorization};return a!==void 0&&(e={...e,vercelConnect:a}),o!==void 0&&(e={...e,displayName:o}),e}let
|
|
1
|
+
function validateAuthorizationSpec(e,t=`auth`){if(typeof e!=`object`||!e)return`The "${t}" field must be an object with a "getToken" method.`;let n=e;if(typeof n.getToken!=`function`)return`The "${t}.getToken" field must be a function returning Promise<{ token }>.`;let r=n.startAuthorization!==void 0,i=n.completeAuthorization!==void 0;if(!r&&!i&&n.principalType!==void 0&&n.principalType!==`app`&&n.principalType!==`user`)return`The "${t}.principalType" field must be "app" or "user".`;if(r!==i)return`The "${t}" field must provide either both "startAuthorization" and "completeAuthorization" (interactive OAuth) or neither (getToken-only). Got only "${r?`startAuthorization`:`completeAuthorization`}".`;if(r&&typeof n.startAuthorization!=`function`)return`The "${t}.startAuthorization" field must be a function when provided.`;if(i&&typeof n.completeAuthorization!=`function`)return`The "${t}.completeAuthorization" field must be a function when provided.`;if(r&&n.principalType!==`user`)return`Interactive authorization (startAuthorization + completeAuthorization) is restricted to "principalType": "user" in v1. App-level credentials must use a getToken-only definition.`;if(n.displayName!==void 0&&(typeof n.displayName!=`string`||n.displayName.length===0))return`The "${t}.displayName" field must be a non-empty string when provided.`}function normalizeAuthorizationSpec(e,t,n=`auth`){let r=validateAuthorizationSpec(e,n);if(r!==void 0)throw Error(`${t} ${r}`);let i=e,a=extractVercelConnectMarker(i.vercelConnect),o=i.displayName,s=typeof i.evict==`function`?i.evict:void 0;if(i.startAuthorization!==void 0&&i.completeAuthorization!==void 0){let e={completeAuthorization:i.completeAuthorization,getToken:i.getToken,principalType:`user`,startAuthorization:i.startAuthorization};return a!==void 0&&(e={...e,vercelConnect:a}),o!==void 0&&(e={...e,displayName:o}),s!==void 0&&(e={...e,evict:s}),e}let c={getToken:i.getToken,principalType:i.principalType??`app`};return a!==void 0&&(c={...c,vercelConnect:a}),o!==void 0&&(c={...c,displayName:o}),s!==void 0&&(c={...c,evict:s}),c}function extractVercelConnectMarker(e){if(typeof e!=`object`||!e)return;let t=e.connector;if(!(typeof t!=`string`||t.length===0))return{connector:t}}export{normalizeAuthorizationSpec,validateAuthorizationSpec};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{stampChallengeDisplayName}from"#runtime/connections/scoped-authorization.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";const logger=createLogger(`framework.connection-search-dynamic`),CONNECTION_SEARCH_OUTPUT_SCHEMA={items:{additionalProperties:!1,properties:{connection:{type:`string`},description:{type:`string`},error:{type:`string`},inputSchema:{type:`object`},needsAuthorization:{type:`boolean`},outputSchema:{type:`object`},qualifiedName:{type:`string`},tool:{type:`string`}},required:[`connection`,`description`],type:`object`},type:`array`},ConnectionSearchResultsKey=new ContextKey(`eve.connectionSearchResults`);function qualifiedConnectionToolName(e,t){return`${e}__${t}`}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n?.authorization&&supportsInteractiveAuthorization(n.authorization))return n.authorization}async function completePendingAuthorizations(e){let n=loadContext(),r=new Set;for(let t of e.getConnections()){let i=getAuthorizationResult(t.connectionName);if(!i)continue;let a=resolveInteractiveAuth(e,t.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(t.connectionName,a),c=await a.completeAuthorization({callbackUrl:i.hookUrl,connection:{url:t.url??``},principal:o,resume:i.resume,callback:i.callback});writeCachedToken(n,t.connectionName,principalKey(o),c),r.add(t.connectionName)}return r}async function executeConnectionSearch(e){let n=loadContext(),i=n.get(ConnectionRegistryKey);if(i===void 0)return[];let s=await completePendingAuthorizations(i),l=e.limit??10,u=tokenize(e.keywords),d=[],p=[],h=e.connection!==void 0&&e.connection!==``?i.getConnections().filter(t=>t.connectionName===e.connection):i.getConnections(),g=[];for(let e of h){let t;try{t=await i.getClient(e.connectionName).getToolMetadata()}catch(t){if(isConnectionAuthorizationRequiredError(t)){if(s.has(e.connectionName)){logger.warn(`connection still unauthorized after authorization`,{connection:e.connectionName}),p.push({connection:e.connectionName,description:e.description,error:`Authorization for "${e.connectionName}" did not take effect; the token was rejected after sign-in.`});continue}let t=resolveInteractiveAuth(i,e.connectionName);if(t){let n=getHookUrl(e.connectionName);if(n){let r=resolveConnectionPrincipal(e.connectionName,t);try{let{challenge:i,resume:a}=await t.startAuthorization({callbackUrl:n,connection:{url:e.url??``},principal:r});g.push({name:e.connectionName,challenge:stampChallengeDisplayName(i,t),hookUrl:n,resume:a})}catch(t){logger.warn(`startAuthorization failed`,{connection:e.connectionName,error:t instanceof Error?t:Error(String(t))})}}}p.push({connection:e.connectionName,description:e.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(t)){logger.warn(`connection authorization failed`,{connection:e.connectionName,reason:t.reason,retryable:t.retryable,error:t}),p.push({connection:e.connectionName,description:e.description,error:`Authorization failed for ${e.connectionName}: ${t.message}`});continue}let n=t instanceof Error?t.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:e.connectionName,error:t instanceof Error?t:Error(n)}),p.push({connection:e.connectionName,description:e.description,error:`Failed to load tools for "${e.connectionName}": ${n}`});continue}for(let n of t){let t=scoreMatch(u,n);t>0&&d.push({item:{connection:e.connectionName,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,qualifiedName:qualifiedConnectionToolName(e.connectionName,n.name),tool:n.name},score:t})}}if(g.length>0)return requestAuthorization(g);d.sort((e,t)=>t.score-e.score);let _=d.slice(0,l).map(e=>e.item);if(_.length>0){let e=[..._,...p],t=n.get(ConnectionSearchResultsKey)??[],r=new Map(t.map(e=>[e.qualifiedName,e]));for(let e of _)e.qualifiedName&&r.set(e.qualifiedName,e);return n.set(ConnectionSearchResultsKey,[...r.values()]),e}return h.map(e=>p.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}function extractDiscoveredTools(e){let t=new Map;for(let n of e){if(n.role!==`tool`)continue;let e=n.content;for(let n of e){if(n.type!==`tool-result`||n.toolName!==`connection_search`)continue;let e=n.output;if(e==null)continue;let r=typeof e==`object`&&`type`in e&&`value`in e?e.value:e;if(Array.isArray(r))for(let e of r)e.tool&&e.qualifiedName&&t.set(e.qualifiedName,e)}}return[...t.values()]}function createConnectionSearchEvents(){return{"step.started":async(e,n)=>{let a=loadContext().get(ConnectionRegistryKey);if(!a||a.getConnections().length===0)return null;let f=a.getConnections().map(e=>e.connectionName),h=extractDiscoveredTools(n.messages),g=loadContext().get(ConnectionSearchResultsKey)??[],_=new Map;for(let e of g)e.qualifiedName&&_.set(e.qualifiedName,e);for(let e of h)e.qualifiedName&&_.set(e.qualifiedName,e);let v=[..._.values()],y={};y.connection_search={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. \`linear__list_issues\`) in your next response. Available connections: ${f.join(`, `)}.`,inputSchema:{type:`object`,additionalProperties:!1,properties:{keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`},connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`}},required:[`keywords`]},async execute(e){return executeConnectionSearch(e)},outputSchema:CONNECTION_SEARCH_OUTPUT_SCHEMA};for(let e of v){let n=e.connection,f=e.tool,p=a.getConnectionApproval(n);y[qualifiedConnectionToolName(n,f)]={description:e.description,inputSchema:e.inputSchema??{type:`object`},needsApproval:p,outputSchema:e.outputSchema,async execute(e){let a=loadContext().get(ConnectionRegistryKey),p=a.getConnections().find(e=>e.connectionName===n),m=p?.authorization&&supportsInteractiveAuthorization(p.authorization)?p.authorization:void 0,h=!1;if(m){let e=getAuthorizationResult(n);if(e){h=!0;let r=loadContext(),i=resolveConnectionPrincipal(n,m),a=await m.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:p?.url??``},principal:i,resume:e.resume,callback:e.callback});writeCachedToken(r,n,principalKey(i),a)}}try{return await a.getClient(n).executeTool(f,e)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!m)throw e;if(h)throw new ConnectionAuthorizationFailedError(n,{retryable:!1,reason:`token_rejected_after_authorization`,message:`Connection "${n}" rejected the token immediately after authorization.`});let t=getHookUrl(n);if(!t)throw e;let r=resolveConnectionPrincipal(n,m),{challenge:a,resume:s}=await m.startAuthorization({callbackUrl:t,connection:{url:p?.url??``},principal:r});return requestAuthorization([{name:n,challenge:stampChallengeDisplayName(a,m),hookUrl:t,resume:s}])}}}}return y}}}function createConnectionSearchResolver(){let e=createConnectionSearchEvents();return{slug:`connection`,eventNames:Object.keys(e),events:e,sourceId:`eve:connection-search-dynamic`,sourceKind:`module`,logicalPath:`eve:framework/connection-search-dynamic`}}export{createConnectionSearchEvents,createConnectionSearchResolver,extractDiscoveredTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{
|
|
1
|
+
import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{registerDefinitionSource,stampDefinitionKey}from"#public/tool-result-narrowing.js";async function resolveToolDefinition(i,a,o){try{let n=expectObjectRecord(await loadResolvedModuleExport({definition:i,kindLabel:`tool`,moduleMap:a,nodeId:o}),describe(i,`to return an object`)),r={kind:`tool`,logicalPath:i.logicalPath,name:i.name},s=`tool-source:${i.sourceId}`;stampDefinitionKey(n,s),registerDefinitionSource(s,r),registerDefinitionSource(`tool:${n.description}`,r);let c=expectFunction(n.execute,describe(i,`to provide an execute function`));return{description:i.description,execute:c,exportName:i.exportName,inputSchema:i.inputSchema,logicalPath:i.logicalPath,name:i.name,outputSchema:i.outputSchema,sourceId:i.sourceId,sourceKind:`module`,...extractOptionalHooks(n,i)}}catch(e){throw e instanceof ResolveAgentError?e:new ResolveAgentError(`Failed to attach the tool execute function from "${i.logicalPath}": ${toErrorMessage(e)}`,{logicalPath:i.logicalPath,sourceId:i.sourceId})}}function extractOptionalHooks(t,n){let r={};return t.needsApproval!==void 0&&(r.needsApproval=expectFunction(t.needsApproval,describe(n,`to provide a needsApproval function`))),t.toModelOutput!==void 0&&(r.toModelOutput=expectFunction(t.toModelOutput,describe(n,`to provide a toModelOutput function`))),t.inputSchema!==void 0&&isFlexibleSchema(t.inputSchema)&&(r.inputStandardSchema=t.inputSchema),t.outputSchema!==void 0&&isFlexibleSchema(t.outputSchema)&&(r.outputStandardSchema=t.outputSchema),r}function describe(e,t){return`Expected the tool export "${e.exportName??`default`}" from "${e.logicalPath}" ${t}.`}function isFlexibleSchema(e){return typeof e==`object`&&!!e&&`~standard`in e&&typeof e[`~standard`]==`object`}export{resolveToolDefinition};
|
|
@@ -154,16 +154,6 @@ export type ResolvedToolDefinition = Readonly<Optional<InternalToolDefinitionWit
|
|
|
154
154
|
* handlers and the stream. See {@link ToolModelOutput}.
|
|
155
155
|
*/
|
|
156
156
|
readonly toModelOutput?: (output: unknown) => ToolModelOutput | Promise<ToolModelOutput>;
|
|
157
|
-
/**
|
|
158
|
-
* Optional authorization strategy reattached from the authored
|
|
159
|
-
* module at resolve time. Carries live `getToken` /
|
|
160
|
-
* `startAuthorization` / `completeAuthorization` callbacks, so it
|
|
161
|
-
* cannot survive compilation and must be read off the resolved
|
|
162
|
-
* module like {@link execute}. When present, the execution layer
|
|
163
|
-
* builds token accessors onto the tool context and drives the
|
|
164
|
-
* interactive consent flow scoped to this tool's {@link name}.
|
|
165
|
-
*/
|
|
166
|
-
readonly auth?: AuthorizationDefinition;
|
|
167
157
|
};
|
|
168
158
|
/**
|
|
169
159
|
* Runtime-owned authored hook definition resolved from a compiled module
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { appendEnv } from "./append-env.js";
|
|
2
|
+
export declare const AI_GATEWAY_API_KEY_ENV_VAR = "AI_GATEWAY_API_KEY";
|
|
3
|
+
export declare const AI_GATEWAY_API_KEY_ENV_FILE = ".env.local";
|
|
4
|
+
/** The location written by eve when a user supplies an AI Gateway API key. */
|
|
5
|
+
export interface AiGatewayApiKeyLocation {
|
|
6
|
+
envKey: typeof AI_GATEWAY_API_KEY_ENV_VAR;
|
|
7
|
+
envFile: typeof AI_GATEWAY_API_KEY_ENV_FILE;
|
|
8
|
+
envPath: string;
|
|
9
|
+
}
|
|
10
|
+
/** Trims and saves an explicit AI Gateway API key, replacing any prior value. */
|
|
11
|
+
export declare function writeAiGatewayApiKey(input: {
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
appendEnv?: typeof appendEnv;
|
|
15
|
+
}): Promise<AiGatewayApiKeyLocation>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{appendEnv}from"./append-env.js";import{join}from"node:path";const AI_GATEWAY_API_KEY_ENV_VAR=`AI_GATEWAY_API_KEY`,AI_GATEWAY_API_KEY_ENV_FILE=`.env.local`;async function writeAiGatewayApiKey(e){let t=join(e.projectRoot,AI_GATEWAY_API_KEY_ENV_FILE);return await(e.appendEnv??appendEnv)(t,{[AI_GATEWAY_API_KEY_ENV_VAR]:e.apiKey.trim()},{force:!0}),{envKey:AI_GATEWAY_API_KEY_ENV_VAR,envFile:AI_GATEWAY_API_KEY_ENV_FILE,envPath:t}}export{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR,writeAiGatewayApiKey};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appendEnv}from"../append-env.js";import{isProjectResolved}from"../project-resolution.js";import{requireProjectPath}from"../state.js";import{
|
|
1
|
+
import{appendEnv}from"../append-env.js";import{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR,writeAiGatewayApiKey}from"../ai-gateway-api-key.js";import{isProjectResolved}from"../project-resolution.js";import{requireProjectPath}from"../state.js";import{withSpinner}from"../with-spinner.js";import{runVercelEnvPull}from"../run-vercel-link.js";import{detectAiGatewayResolution}from"./detect-ai-gateway.js";import{createPromptCommandOutput}from"#setup/cli/index.js";function applyAiGatewayCredential(a){let o=a.deps??{appendEnv,runVercelEnvPull,detectAiGatewayResolution};return{id:`apply-ai-gateway-credential`,shouldRun(e){let{aiGateway:t,project:n}=e;return t.kind===`byok`||t.kind===`inherit`&&isProjectResolved(n)},async gather(){return null},async perform({state:e,signal:r}){let{prompter:i}=a,s=requireProjectPath(e),c=e.aiGateway,l=isProjectResolved(e.project);if(c.kind===`byop`)return{kind:`unresolved`};if(c.kind===`byok`){let e=await writeAiGatewayApiKey({projectRoot:s,apiKey:c.apiGatewayKey,appendEnv:o.appendEnv});return a.prompter.log.success(`Wrote ${e.envKey} to ${e.envPath}`),{kind:`api-key`,envFile:e.envFile}}if(!l)return i.log.warning(`No Vercel project linked and no API key provided. The agent will not reach a model until you set ${AI_GATEWAY_API_KEY_ENV_VAR} in ${AI_GATEWAY_API_KEY_ENV_FILE} or link a project.`),{kind:`unresolved`};let u=createPromptCommandOutput(i.log),d=await withSpinner(i,`Pulling Vercel environment variables into .env.local...`,()=>o.runVercelEnvPull(s,u,r));return r?.throwIfAborted(),d||i.log.warning(`Linked the project, but pulling environment variables did not complete.`),o.detectAiGatewayResolution(s)},apply(e,t){return{...e,aiGatewayCredentials:t}}}}export{applyAiGatewayCredential};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{requireProjectPath}from"../state.js";import{join}from"node:path";import{readFile}from"node:fs/promises";const ENV_FILE_CANDIDATES=[
|
|
2
|
-
`))if(readEnvValue(e,t)!==void 0)return!0;return!1}async function findEnvFileWithKey(e,
|
|
1
|
+
import{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR}from"../ai-gateway-api-key.js";import{requireProjectPath}from"../state.js";import{join}from"node:path";import{readFile}from"node:fs/promises";const ENV_FILE_CANDIDATES=[AI_GATEWAY_API_KEY_ENV_FILE,`.env`];function readEnvValue(e,t){let n=e.trim();if(n.length===0||n.startsWith(`#`)||!n.startsWith(`${t}=`))return;let r=n.slice(t.length+1);if(r.length===0)return;let i=r.length>=2&&r.startsWith(`"`)&&r.endsWith(`"`)?r.slice(1,-1):r;return i.length===0?void 0:i}async function hasApiKeyInFile(e,t){let n;try{n=await readFile(e,`utf8`)}catch{return!1}for(let e of n.split(`
|
|
2
|
+
`))if(readEnvValue(e,t)!==void 0)return!0;return!1}async function findEnvFileWithKey(e,t){for(let n of ENV_FILE_CANDIDATES)if(await hasApiKeyInFile(join(e,n),t))return n}async function detectAiGatewayResolution(e){let n=await findEnvFileWithKey(e,AI_GATEWAY_API_KEY_ENV_VAR);return n===void 0?{kind:`unresolved`}:{kind:`api-key`,envFile:n}}function detectAiGateway(){return{id:`detect-ai-gateway`,shouldRun(e){return e.projectPath.kind===`resolved`},async gather(){return null},async perform({state:e}){return detectAiGatewayResolution(requireProjectPath(e))},apply(e,t){return{...e,aiGatewayCredentials:t}}}}export{detectAiGateway,detectAiGatewayResolution,findEnvFileWithKey};
|
|
@@ -2,12 +2,11 @@ import { detectProjectResolution, type ProjectResolution } from "../project-reso
|
|
|
2
2
|
import type { Prompter } from "../prompter.js";
|
|
3
3
|
import { type SetupState } from "../state.js";
|
|
4
4
|
import type { SetupBox } from "../step.js";
|
|
5
|
-
import { linkProject,
|
|
5
|
+
import { linkProject, unresolvedProject } from "../vercel-project.js";
|
|
6
6
|
/** Injected for tests; defaults to the real Vercel project helpers. */
|
|
7
7
|
export interface LinkProjectDeps {
|
|
8
8
|
linkProject: typeof linkProject;
|
|
9
9
|
detectProjectResolution: typeof detectProjectResolution;
|
|
10
|
-
resolveProjectByNameOrId: typeof resolveProjectByNameOrId;
|
|
11
10
|
unresolvedProject: typeof unresolvedProject;
|
|
12
11
|
}
|
|
13
12
|
export interface LinkProjectOptions {
|
|
@@ -16,18 +15,13 @@ export interface LinkProjectOptions {
|
|
|
16
15
|
deps?: LinkProjectDeps;
|
|
17
16
|
}
|
|
18
17
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* resolve-provisioning box, so `perform` owns all the work.
|
|
18
|
+
* Executes the resolved Vercel project plan after scaffolding, once the project
|
|
19
|
+
* directory exists. Gather prompts for nothing — every decision was made in the
|
|
20
|
+
* resolve-provisioning box.
|
|
23
21
|
*
|
|
24
|
-
* The plan is authoritative
|
|
25
|
-
* a stale or mismatched `.vercel` link can
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* in `state.project`, which every later box reads.
|
|
29
|
-
*
|
|
30
|
-
* Named `linkVercelProject` because the setup island also exports the
|
|
31
|
-
* `linkProject` executor helper this box drives.
|
|
22
|
+
* The plan is authoritative: the box always re-links to the planned project so
|
|
23
|
+
* a stale or mismatched `.vercel` link can't silently win. Re-linking the same
|
|
24
|
+
* identity is idempotent. The resolution read back from the link metadata lands
|
|
25
|
+
* in `state.project`.
|
|
32
26
|
*/
|
|
33
27
|
export declare function linkVercelProject(options: LinkProjectOptions): SetupBox<SetupState, null, ProjectResolution>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{detectProjectResolution,mergeProjectResolution}from"../project-resolution.js";import{requireProjectPath}from"../state.js";import{linkProject,
|
|
1
|
+
import{detectProjectResolution,mergeProjectResolution}from"../project-resolution.js";import{requireProjectPath}from"../state.js";import{linkProject,unresolvedProject}from"../vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";function linkVercelProject(a){let o=a.deps??{linkProject,detectProjectResolution,unresolvedProject};return{id:`link-project`,shouldRun(e){return e.vercelProject.kind!==`none`},async gather(){return null},async perform({state:e,signal:t}){let r=e.vercelProject;if(r.kind===`none`)return o.unresolvedProject();let i=requireProjectPath(e),s=createPromptCommandOutput(a.prompter.log),c=await o.linkProject(a.prompter,i,r,s,{signal:t});if(t?.throwIfAborted(),!c)throw Error("Vercel project provisioning did not complete. Run `vercel link` manually, or re-run and choose not to deploy to Vercel.");let l=await o.detectProjectResolution(i,{signal:t});if(l.kind===`unresolved`)throw Error(`Linked the directory, but could not resolve the Vercel project from its link metadata.`);if(l.projectId!==c.projectId)throw Error(`The linked project does not match the selected project: expected ${c.projectId}, found ${l.projectId}.`);return l},apply(e,n){return{...e,project:mergeProjectResolution(e.project,n)}}}}export{linkVercelProject};
|
|
@@ -4,7 +4,7 @@ import { detectProjectResolution, type ProjectResolution } from "../project-reso
|
|
|
4
4
|
import type { Prompter } from "../prompter.js";
|
|
5
5
|
import type { ProvisioningMode, ResolvedAiGateway, ResolvedVercelProject, SetupState, WiringMode } from "../state.js";
|
|
6
6
|
import type { SetupBox } from "../step.js";
|
|
7
|
-
import { assertNewProjectNameAvailable, isVercelAuthenticated, pickNewProjectName, pickProject, pickTeam, requireAuth, resolveTeam, validateTeam } from "../vercel-project.js";
|
|
7
|
+
import { assertNewProjectNameAvailable, isVercelAuthenticated, pickNewProjectName, pickProject, pickTeam, requireAuth, resolveProjectByNameOrId, resolveTeam, validateTeam } from "../vercel-project.js";
|
|
8
8
|
/** Injected for tests; defaults to the real Vercel project and fs helpers. */
|
|
9
9
|
export interface ResolveProvisioningDeps {
|
|
10
10
|
requireAuth: typeof requireAuth;
|
|
@@ -17,6 +17,7 @@ export interface ResolveProvisioningDeps {
|
|
|
17
17
|
pickProject: typeof pickProject;
|
|
18
18
|
pickNewProjectName: typeof pickNewProjectName;
|
|
19
19
|
assertNewProjectNameAvailable: typeof assertNewProjectNameAvailable;
|
|
20
|
+
resolveProjectByNameOrId: typeof resolveProjectByNameOrId;
|
|
20
21
|
}
|
|
21
22
|
export interface ResolveProvisioningOptions {
|
|
22
23
|
/** Resolves the provisioning-tree questions; the composed stack decides how. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveTeam,validateTeam
|
|
1
|
+
import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{withSpinner}from"../with-spinner.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveProjectByNameOrId,resolveTeam,validateTeam}from"../vercel-project.js";import{pathExists}from"../path-exists.js";import{join,resolve}from"node:path";import{byokProviderEnvVar}from"#setup/scaffold/index.js";import{whimsyFor}from"#setup/cli/index.js";function vercelDemands(e){return{slack:e.channelSelection.includes(`slack`),connectSlugs:e.connectionSelection.filter(e=>e.entry.auth?.kind===`connect`).map(e=>e.slug)}}function connectClause(e){let t=e.length===1?`authenticates`:`authenticate`;return`${e.join(`, `)} ${t} through Vercel Connect`}function vercelRequirements(e){let t=[];return e.slack&&t.push(`Slack needs a public URL`),e.connectSlugs.length>0&&t.push(connectClause(e.connectSlugs)),t}function resolveProvisioning(l){let u=l.deps??{requireAuth,isVercelAuthenticated,detectProjectResolution,pathExists,validateTeam,resolveTeam,pickTeam,pickProject,pickNewProjectName,assertNewProjectNameAvailable,resolveProjectByNameOrId},parent=()=>resolve(l.targetDirectory??process.cwd());async function detectAdoptableLink(e,t){if(e.projectPath.kind!==`resolved`)return;let n=e.projectPath.path;if(await u.pathExists(join(n,`.vercel`,`project.json`)))return withSpinner(l.prompter,whimsyFor(`project-detect`),async()=>{let e=await u.detectProjectResolution(n,{signal:t});if(!(!isProjectResolved(e)||!await u.isVercelAuthenticated(n,{signal:t})))return e})}async function plansFromFlags(e,t,n,r){if(t.skipVercel)return n.apiKey===void 0?{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:n.apiKey},modelWiring:`gateway`};if(t.team===void 0||t.team.length===0)throw Error(`Headless Vercel provisioning requires --team <slug> or --scope <slug> so the current CLI scope is not applied silently.`);await u.requireAuth(parent(),void 0,{signal:r}),await u.validateTeam(l.prompter,parent(),t.team,{signal:r});let i=await u.resolveTeam(parent(),t.team,{signal:r}),a=n.apiKey===void 0?{kind:`inherit`}:{kind:`byok`,apiGatewayKey:n.apiKey},o;if(t.project===void 0)o={kind:`new`,project:e,team:i};else{let e=await u.resolveProjectByNameOrId(parent(),i,t.project,{signal:r});if(e===null)throw Error(`Vercel project "${t.project}" was not found in ${i}.`);o={kind:`existing`,project:e,team:i}}return o.kind===`new`&&await u.assertNewProjectNameAvailable(parent(),i,o.project,{signal:r}),{vercelProject:o,aiGateway:a,modelWiring:`gateway`}}async function promptPlans(n,r){let i=l.prompter,a=n.agentName,o=l.adoptExistingLink===!1?void 0:await detectAdoptableLink(n,r);if(o!==void 0)return i.log.info(`This directory is already linked to a Vercel project, so your agent will run there — the AI Gateway authenticates through your Vercel login.`),{vercelProject:{kind:`none`},aiGateway:{kind:`inherit`},modelWiring:`gateway`,project:o};let s=vercelRequirements(vercelDemands(n)),c=!0;if(s.length>0?i.log.info(`${s.join(` and `)}, so your agent will run on Vercel.`):c=await l.asker.ask(select({key:`deploy`,message:`Where should your agent run?`,options:[{id:`vercel`,value:!0,label:`On Vercel`,hint:`AI Gateway, Durable Workflow, Sandbox, and more`},{id:`local`,value:!1,label:`Locally for now`,hint:`run with eve dev, deploy any time later`}],recommended:!0,required:!0})),c){await u.requireAuth(parent(),i,{signal:r});let t=await u.pickTeam(i,parent(),void 0,{signal:r}),n=[{value:`new`,label:`Create a new project`,hint:`Named ${a}`},{value:`link`,label:`Link an existing project`}],o=l.projectSelection!==`existing-only`&&l.prompter.selectEditable?await l.prompter.selectEditable({message:`Vercel project`,options:n,initialValue:`new`,editable:{value:`new`,defaultValue:a,formatHint:e=>`Named ${e}`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}}):void 0;if((l.projectSelection===`existing-only`?`link`:o?.value??await l.asker.ask(select({key:`vercel-project`,message:`Vercel project`,options:[{id:`new`,value:`new`,label:`Create a new project`,hint:`Named ${a}`},{id:`link`,value:`link`,label:`Link an existing project`}],recommended:`new`,required:!0})))===`new`){let e=o?.kind===`edited`?o.text:a;return{vercelProject:{kind:`new`,project:await u.pickNewProjectName(i,parent(),t,e,{signal:r}),team:t},aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return{vercelProject:await u.pickProject(i,parent(),t,{allowCreateWhenEmpty:l.projectSelection!==`existing-only`,signal:r}),aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return await l.asker.ask(select({key:`credential`,message:`How should your agent reach the model?`,options:[{id:`api-key`,value:`api-key`,label:`Use my own AI Gateway API key`,hint:`AI_GATEWAY_API_KEY`},{id:`local`,value:`local`,label:`Use my own provider API key`,hint:byokProviderEnvVar(n.modelId)}],recommended:`api-key`,required:!0}))===`api-key`?{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:await l.asker.ask(text({key:`gateway-api-key`,message:`Enter your AI_GATEWAY_API_KEY`,sensitive:!0,validate:e=>e.trim().length===0?`API key cannot be empty.`:null,required:!0}))},modelWiring:`gateway`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}}return{id:`resolve-provisioning`,async gather({state:e,signal:t}){if(l.mode.headless){let n=await plansFromFlags(e.agentName,l.mode.project,l.mode.aiGateway,t);if(n.vercelProject.kind===`none`){let t=vercelDemands(e);if(t.slack)throw Error(`Slack requires a Vercel project. Remove --skip-vercel to add Slack.`);if(t.connectSlugs.length>0)throw Error(`${connectClause(t.connectSlugs)}, which needs a Vercel project. Remove --skip-vercel to add ${t.connectSlugs.length===1?`it`:`them`}.`)}return n}return promptPlans(e,t)},async perform({input:e}){return e},apply(e,t){return{...e,vercelProject:t.vercelProject,aiGateway:t.aiGateway,modelWiring:t.modelWiring,project:t.project===void 0?e.project:mergeProjectResolution(e.project,t.project)}}}}export{resolveProvisioning};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Prompt } from "@clack/core";
|
|
2
2
|
import { type PromptOption, type PromptValue } from "./prompt-ui.js";
|
|
3
|
-
import { type SelectState } from "./select-state.js";
|
|
3
|
+
import { type SearchActionOption, type SelectState } from "./select-state.js";
|
|
4
4
|
/**
|
|
5
5
|
* A prompt's live status note (e.g. a two-stage quit guard), read on each
|
|
6
6
|
* render. Returns `undefined` when there is nothing to show. Wired in by
|
|
@@ -20,6 +20,7 @@ export declare class SelectComponent extends Prompt<string | string[]> {
|
|
|
20
20
|
readonly options: PromptOption<string>[];
|
|
21
21
|
readonly multiple: boolean;
|
|
22
22
|
readonly search: boolean;
|
|
23
|
+
readonly searchAction: SearchActionOption | undefined;
|
|
23
24
|
readonly required: boolean;
|
|
24
25
|
filter: string;
|
|
25
26
|
optionCursor: number;
|
|
@@ -28,6 +29,7 @@ export declare class SelectComponent extends Prompt<string | string[]> {
|
|
|
28
29
|
options: PromptOption<string>[];
|
|
29
30
|
multiple: boolean;
|
|
30
31
|
search: boolean;
|
|
32
|
+
searchAction?: SearchActionOption;
|
|
31
33
|
required: boolean;
|
|
32
34
|
initial: SelectState;
|
|
33
35
|
render: (this: Omit<SelectComponent, "prompt">) => string | undefined;
|
|
@@ -74,6 +76,10 @@ export declare function runSelectComponent<T extends PromptValue>(input: {
|
|
|
74
76
|
options: readonly PromptOption<T>[];
|
|
75
77
|
multiple: boolean;
|
|
76
78
|
search: boolean;
|
|
79
|
+
searchAction?: {
|
|
80
|
+
label(query: string): string;
|
|
81
|
+
value(query: string): T;
|
|
82
|
+
};
|
|
77
83
|
required: boolean;
|
|
78
84
|
placeholder?: string;
|
|
79
85
|
defaultValue?: T;
|