eve 0.31.2 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/src/chunks/{use-eve-agent-CF5D6jSV.js → use-eve-agent-CX87G1vU.js} +30 -5
  3. package/dist/src/chunks/{use-eve-agent-DbYZLG0h.js → use-eve-agent-DwyRde1N.js} +30 -5
  4. package/dist/src/cli/commands/integration-connect.d.ts +1 -0
  5. package/dist/src/cli/commands/integration-connect.js +1 -1
  6. package/dist/src/cli/commands/integration-setup.d.ts +4 -0
  7. package/dist/src/cli/commands/integration-setup.js +1 -1
  8. package/dist/src/cli/commands/register-integration-commands.js +1 -1
  9. package/dist/src/cli/commands/register-registry-commands.js +1 -1
  10. package/dist/src/cli/commands/registry-declared-setups.d.ts +23 -0
  11. package/dist/src/cli/commands/registry-declared-setups.js +1 -0
  12. package/dist/src/cli/commands/registry-metadata.d.ts +69 -0
  13. package/dist/src/cli/commands/registry-metadata.js +1 -1
  14. package/dist/src/cli/commands/registry-package.d.ts +3 -3
  15. package/dist/src/cli/commands/registry-package.js +1 -1
  16. package/dist/src/cli/commands/registry-presentation.d.ts +20 -0
  17. package/dist/src/cli/commands/registry-presentation.js +7 -0
  18. package/dist/src/cli/commands/registry-search-presentation.d.ts +16 -0
  19. package/dist/src/cli/commands/registry-search-presentation.js +6 -0
  20. package/dist/src/cli/commands/registry-setup-command.d.ts +5 -2
  21. package/dist/src/cli/commands/registry-setup-command.js +1 -2
  22. package/dist/src/cli/commands/registry.d.ts +16 -7
  23. package/dist/src/cli/commands/registry.js +1 -6
  24. package/dist/src/cli/commands/setup-answers.d.ts +2 -0
  25. package/dist/src/cli/commands/setup-answers.js +1 -0
  26. package/dist/src/cli/commands/setup-headless.d.ts +79 -0
  27. package/dist/src/cli/commands/setup-headless.js +1 -0
  28. package/dist/src/cli/dev/tui/runner.js +1 -1
  29. package/dist/src/cli/dev/tui/setup-commands.js +3 -3
  30. package/dist/src/cli/dev/tui/setup-flow.d.ts +8 -1
  31. package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
  32. package/dist/src/cli/dev/tui/traces/trace-conversation.d.ts +1 -1
  33. package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
  34. package/dist/src/client/url.d.ts +5 -0
  35. package/dist/src/client/url.js +1 -1
  36. package/dist/src/compiled/.vendor-stamp.json +8 -8
  37. package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +33 -13
  38. package/dist/src/compiled/@ai-sdk/anthropic/index.js +4 -2
  39. package/dist/src/compiled/@ai-sdk/google/index.d.ts +50 -3
  40. package/dist/src/compiled/@ai-sdk/google/index.js +7 -7
  41. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +33 -3
  42. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  43. package/dist/src/compiled/@ai-sdk/openai/index.d.ts +140 -9
  44. package/dist/src/compiled/@ai-sdk/openai/index.js +9 -6
  45. package/dist/src/compiled/@ai-sdk/otel/index.js +1 -1
  46. package/dist/src/compiled/@ai-sdk/provider/index.d.ts +300 -6
  47. package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +70 -20
  48. package/dist/src/compiled/@opentelemetry/api/index.d.ts +2 -1
  49. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/index.js +1 -1
  50. package/dist/src/compiled/@vercel/otel/index.d.ts +6 -0
  51. package/dist/src/compiled/@vercel/sandbox/index.js +1 -1
  52. package/dist/src/compiled/_chunks/node/undici-CUIybhQ2.js +95 -0
  53. package/dist/src/compiled/_chunks/workflow/dist-eUxFublw.js +77 -0
  54. package/dist/src/compiled/shadcn-registry/index.js +309 -215
  55. package/dist/src/compiler/extension-compatibility.d.ts +10 -10
  56. package/dist/src/compiler/extension-compatibility.js +1 -1
  57. package/dist/src/context/build-callback-context.js +1 -1
  58. package/dist/src/execution/authorization-callback-match.d.ts +14 -0
  59. package/dist/src/execution/authorization-callback-match.js +1 -0
  60. package/dist/src/execution/next-driver-action.d.ts +1 -0
  61. package/dist/src/execution/parked-delivery-wait.d.ts +16 -1
  62. package/dist/src/execution/parked-delivery-wait.js +1 -1
  63. package/dist/src/execution/sandbox/abort-bound-session.d.ts +1 -1
  64. package/dist/src/execution/sandbox/bindings/docker.js +1 -1
  65. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  66. package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
  67. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.d.ts +1 -0
  68. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
  69. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  70. package/dist/src/execution/sandbox/ensure.js +1 -1
  71. package/dist/src/execution/session-command-inbox.d.ts +26 -1
  72. package/dist/src/execution/session-command-inbox.js +1 -1
  73. package/dist/src/execution/turn-execution-cursor.d.ts +1 -0
  74. package/dist/src/execution/turn-workflow.js +1 -1
  75. package/dist/src/execution/workflow-entry.js +1 -1
  76. package/dist/src/execution/workflow-steps.d.ts +3 -4
  77. package/dist/src/execution/workflow-steps.js +1 -1
  78. package/dist/src/harness/authorization.d.ts +24 -11
  79. package/dist/src/harness/authorization.js +1 -1
  80. package/dist/src/harness/input-extraction.js +1 -1
  81. package/dist/src/harness/input-requests.js +1 -1
  82. package/dist/src/harness/provider-tools.d.ts +1 -1
  83. package/dist/src/harness/provider-tools.js +1 -1
  84. package/dist/src/harness/tool-loop.js +2 -2
  85. package/dist/src/internal/application/package.js +1 -1
  86. package/dist/src/internal/authored-definition/connection.js +1 -1
  87. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  88. package/dist/src/protocol/routes.d.ts +8 -6
  89. package/dist/src/protocol/routes.js +1 -1
  90. package/dist/src/public/channels/linear/linearChannel.d.ts +2 -0
  91. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  92. package/dist/src/public/channels/linear/verify.js +1 -1
  93. package/dist/src/public/channels/slack/attachments.js +1 -1
  94. package/dist/src/public/channels/slack/defaults.js +1 -1
  95. package/dist/src/public/channels/slack/hitl.d.ts +14 -1
  96. package/dist/src/public/channels/slack/hitl.js +2 -2
  97. package/dist/src/public/connections/index.d.ts +1 -0
  98. package/dist/src/public/definitions/callback-context.d.ts +2 -2
  99. package/dist/src/public/definitions/connections/mcp.d.ts +9 -0
  100. package/dist/src/public/definitions/connections/openapi.d.ts +9 -0
  101. package/dist/src/public/definitions/connections/tool-call.d.ts +21 -0
  102. package/dist/src/public/definitions/connections/tool-call.js +1 -0
  103. package/dist/src/public/definitions/sandbox.d.ts +1 -1
  104. package/dist/src/public/definitions/tool.d.ts +9 -9
  105. package/dist/src/public/sandbox/index.d.ts +1 -1
  106. package/dist/src/public/tools/web-search.d.ts +2 -2
  107. package/dist/src/runtime/connections/callback-route.d.ts +4 -5
  108. package/dist/src/runtime/connections/callback-route.js +1 -1
  109. package/dist/src/runtime/connections/mcp-client.js +1 -1
  110. package/dist/src/runtime/connections/openapi-client.js +1 -1
  111. package/dist/src/runtime/connections/provided-arguments.d.ts +9 -0
  112. package/dist/src/runtime/connections/provided-arguments.js +1 -0
  113. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  114. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -2
  115. package/dist/src/runtime/framework-tools/todo.js +1 -1
  116. package/dist/src/runtime/input/types.d.ts +1 -4
  117. package/dist/src/runtime/resolve-connection.js +1 -1
  118. package/dist/src/runtime/types.d.ts +2 -0
  119. package/dist/src/sandbox/state.d.ts +1 -0
  120. package/dist/src/setup/ask-editable.d.ts +22 -0
  121. package/dist/src/setup/ask-editable.js +1 -0
  122. package/dist/src/setup/ask-signals.d.ts +23 -0
  123. package/dist/src/setup/ask-signals.js +1 -0
  124. package/dist/src/setup/ask.d.ts +24 -25
  125. package/dist/src/setup/ask.js +1 -1
  126. package/dist/src/setup/boxes/deploy-project.js +1 -1
  127. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  128. package/dist/src/setup/connection-connector.js +3 -2
  129. package/dist/src/setup/flows/registry-session.d.ts +33 -0
  130. package/dist/src/setup/flows/registry-session.js +1 -0
  131. package/dist/src/setup/flows/registry.d.ts +16 -0
  132. package/dist/src/setup/flows/registry.js +1 -1
  133. package/dist/src/setup/index.js +1 -1
  134. package/dist/src/setup/integrations/discord/setup.d.ts +19 -7
  135. package/dist/src/setup/integrations/discord/setup.js +4 -3
  136. package/dist/src/setup/integrations/github/setup.d.ts +36 -7
  137. package/dist/src/setup/integrations/github/setup.js +2 -2
  138. package/dist/src/setup/integrations/linear/setup.d.ts +21 -9
  139. package/dist/src/setup/integrations/linear/setup.js +2 -2
  140. package/dist/src/setup/integrations/photon/management.d.ts +8 -1
  141. package/dist/src/setup/integrations/photon/management.js +1 -1
  142. package/dist/src/setup/integrations/photon/setup-flow.d.ts +28 -24
  143. package/dist/src/setup/integrations/photon/setup-flow.js +3 -3
  144. package/dist/src/setup/integrations/photon/setup.d.ts +1 -3
  145. package/dist/src/setup/integrations/photon/setup.js +1 -1
  146. package/dist/src/setup/integrations/registry.d.ts +1 -1
  147. package/dist/src/setup/integrations/registry.js +1 -1
  148. package/dist/src/setup/integrations/runner.d.ts +14 -11
  149. package/dist/src/setup/integrations/runner.js +1 -1
  150. package/dist/src/setup/integrations/shared/prerequisite.d.ts +17 -0
  151. package/dist/src/setup/integrations/shared/prerequisite.js +1 -0
  152. package/dist/src/setup/integrations/shared/ui.d.ts +22 -15
  153. package/dist/src/setup/integrations/shared/ui.js +2 -2
  154. package/dist/src/setup/integrations/shared/vercel-project.d.ts +11 -0
  155. package/dist/src/setup/integrations/shared/vercel-project.js +1 -0
  156. package/dist/src/setup/integrations/slack/setup.d.ts +25 -10
  157. package/dist/src/setup/integrations/slack/setup.js +1 -1
  158. package/dist/src/setup/integrations/types.d.ts +43 -13
  159. package/dist/src/setup/integrations/types.js +1 -1
  160. package/dist/src/setup/integrations/web/setup.d.ts +22 -3
  161. package/dist/src/setup/integrations/web/setup.js +1 -1
  162. package/dist/src/setup/prompter.d.ts +8 -0
  163. package/dist/src/setup/question-options.d.ts +15 -0
  164. package/dist/src/setup/question-options.js +1 -0
  165. package/dist/src/setup/registry-setup-client.d.ts +3 -4
  166. package/dist/src/setup/registry-setup-client.js +2 -2
  167. package/dist/src/setup/registry-setup-completion.d.ts +3 -0
  168. package/dist/src/setup/registry-setup-completion.js +1 -0
  169. package/dist/src/setup/registry-setup-protocol.d.ts +22 -3
  170. package/dist/src/setup/registry-setup-protocol.js +1 -1
  171. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  172. package/dist/src/setup/scaffold/create/project.js +55 -3
  173. package/dist/src/setup/scaffold/update/channels.js +1 -1
  174. package/dist/src/setup/setup-question-wire.d.ts +48 -0
  175. package/dist/src/setup/setup-question-wire.js +1 -0
  176. package/dist/src/setup/slackbot.d.ts +5 -1
  177. package/dist/src/setup/slackbot.js +1 -1
  178. package/dist/src/setup/state.d.ts +2 -2
  179. package/dist/src/shared/sandbox-backend.d.ts +6 -0
  180. package/dist/src/shared/sandbox-session.d.ts +15 -0
  181. package/dist/src/shared/tool-definition.d.ts +2 -2
  182. package/dist/src/shared/tool-schema.d.ts +5 -4
  183. package/dist/src/shared/tool-schema.js +1 -1
  184. package/dist/src/svelte/index.js +1 -1
  185. package/dist/src/svelte/use-eve-agent.js +1 -1
  186. package/dist/src/tracing/agent-otel-provider.d.ts +5 -38
  187. package/dist/src/tracing/agent-otel-provider.js +2 -2
  188. package/dist/src/tracing/agent-span-id-generator.d.ts +15 -0
  189. package/dist/src/tracing/agent-span-id-generator.js +1 -0
  190. package/dist/src/tracing/agent-trace-context-store.d.ts +1 -1
  191. package/dist/src/tracing/agent-trace-context-store.js +1 -1
  192. package/dist/src/tracing/agent-trace-state.d.ts +41 -0
  193. package/dist/src/tracing/agent-trace-state.js +1 -0
  194. package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
  195. package/dist/src/vue/index.js +1 -1
  196. package/dist/src/vue/use-eve-agent.js +1 -1
  197. package/docs/channels/chat-sdk.mdx +1 -1
  198. package/docs/channels/github.mdx +1 -1
  199. package/docs/channels/linear.mdx +4 -2
  200. package/docs/channels/overview.mdx +2 -2
  201. package/docs/channels/slack.mdx +154 -40
  202. package/docs/concepts/default-harness.md +3 -3
  203. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  204. package/docs/connections/mcp.mdx +35 -1
  205. package/docs/connections/openapi.mdx +24 -0
  206. package/docs/guides/dev-tui.md +58 -142
  207. package/docs/guides/dynamic-workflows.md +2 -0
  208. package/docs/guides/frontend/overview.mdx +4 -2
  209. package/docs/guides/hooks.md +25 -3
  210. package/docs/guides/session-context.md +18 -0
  211. package/docs/install-integrations.mdx +2 -0
  212. package/docs/reference/cli.md +5 -3
  213. package/docs/reference/typescript-api.md +1 -1
  214. package/docs/sandbox.mdx +25 -5
  215. package/docs/skills.mdx +12 -1
  216. package/docs/subagents.mdx +1 -1
  217. package/docs/tools/human-in-the-loop.md +1 -1
  218. package/docs/tutorial/ship-it.mdx +1 -1
  219. package/package.json +10 -10
  220. package/dist/src/compiled/_chunks/node/undici-DWL_MYmT.js +0 -95
  221. package/dist/src/compiled/_chunks/workflow/dist-CZxNEvEs.js +0 -79
@@ -1,19 +1,31 @@
1
- import { ensureVercelProject } from "#setup/flows/ensure-vercel-project.js";
1
+ import type { VercelProjectReference } from "#setup/project-resolution.js";
2
2
  import { deriveSlackConnectorSlug } from "#setup/scaffold/index.js";
3
3
  import { writeTextFile } from "#setup/scaffold/files.js";
4
- import type { IntegrationSetupContext, IntegrationSetupResult, SetupIntegration } from "../types.js";
5
- import { attachLinearConnector, findLinearConnector, provisionLinearConnector } from "./connect.js";
4
+ import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
5
+ import { attachLinearConnector, findLinearConnector, provisionLinearConnector, type LinearConnectorRef } from "./connect.js";
6
6
  export interface LinearSetupDeps {
7
7
  attachConnector: typeof attachLinearConnector;
8
8
  deriveConnectorSlug: typeof deriveSlackConnectorSlug;
9
- ensureVercelProject: typeof ensureVercelProject;
10
9
  findConnector: typeof findLinearConnector;
11
10
  provisionConnector: typeof provisionLinearConnector;
12
11
  writeTextFile: typeof writeTextFile;
13
12
  }
14
- /** Linear does not allow its brand name in a managed app's name. */
15
13
  export declare function linearSafeConnectorSlug(slug: string): string;
16
- /** Runs guided Linear Agent Session connector and channel setup. */
17
- export declare function setupLinear(context: IntegrationSetupContext, deps?: LinearSetupDeps): Promise<IntegrationSetupResult>;
18
- /** Linear Agent Session setup registration. */
19
- export declare const LINEAR_SETUP: SetupIntegration;
14
+ type ConnectorPlan = {
15
+ kind: "reuse";
16
+ connector: LinearConnectorRef;
17
+ } | {
18
+ kind: "create";
19
+ slug: string;
20
+ };
21
+ export interface LinearSetupPlan {
22
+ connector: ConnectorPlan;
23
+ project: VercelProjectReference;
24
+ }
25
+ export declare function prepareLinearSetup(context: SetupPrepareContext, deps?: LinearSetupDeps): Promise<LinearSetupPlan>;
26
+ export declare function applyLinearSetup(plan: LinearSetupPlan, context: SetupApplyContext, deps?: LinearSetupDeps): Promise<{
27
+ facts: never[];
28
+ deploymentRequired: true;
29
+ }>;
30
+ export declare const LINEAR_SETUP: import("../types.js").SetupIntegration;
31
+ export {};
@@ -1,7 +1,7 @@
1
- import{attachLinearConnector,findLinearConnector,provisionLinearConnector}from"./connect.js";import{join}from"node:path";import{deriveSlackConnectorSlug,normalizeSlackConnectorSlug}from"#setup/scaffold/index.js";import{WizardCancelledError}from"#setup/step.js";import{select,text}from"#setup/ask.js";import{ensureVercelProject}from"#setup/flows/ensure-vercel-project.js";import{writeTextFile}from"#setup/scaffold/files.js";const defaultDeps={attachConnector:attachLinearConnector,deriveConnectorSlug:deriveSlackConnectorSlug,ensureVercelProject,findConnector:findLinearConnector,provisionConnector:provisionLinearConnector,writeTextFile};function linearSafeConnectorSlug(e){return normalizeSlackConnectorSlug(e.replaceAll(/linear/gi,``).replace(/[-_]{2,}/g,`-`)||`agent`)}function connectTemplate(e){return`import { connectLinearCredentials } from "@vercel/connect/eve";
1
+ import{defineSetupIntegration}from"../types.js";import{attachLinearConnector,findLinearConnector,provisionLinearConnector}from"./connect.js";import{join}from"node:path";import{deriveSlackConnectorSlug,normalizeSlackConnectorSlug}from"#setup/scaffold/index.js";import{WizardCancelledError}from"#setup/step.js";import{select,text}from"#setup/ask.js";import{writeTextFile}from"#setup/scaffold/files.js";const defaultDeps={attachConnector:attachLinearConnector,deriveConnectorSlug:deriveSlackConnectorSlug,findConnector:findLinearConnector,provisionConnector:provisionLinearConnector,writeTextFile};function linearSafeConnectorSlug(e){return normalizeSlackConnectorSlug(e.replaceAll(/linear/gi,``).replace(/[-_]{2,}/g,`-`)||`agent`)}function connectTemplate(e){return`import { connectLinearCredentials } from "@vercel/connect/eve";
2
2
  import { linearChannel } from "eve/channels/linear";
3
3
 
4
4
  export default linearChannel({
5
5
  credentials: connectLinearCredentials(${JSON.stringify(e)}),
6
6
  });
7
- `}async function chooseConnector(e,t,n){let r=linearSafeConnectorSlug(await t.deriveConnectorSlug(e.appRoot)),i=linearSafeConnectorSlug(await e.ui.asker.ask(text({key:`linear.connector-name`,message:`Name your Linear agent`,recommended:r,validate:e=>e.trim().length===0?`A Linear agent name is required.`:null}))),a=await t.findConnector({project:n,projectRoot:e.appRoot,slug:i,signal:e.signal});if(a!==void 0){let r=await e.ui.asker.ask(select({key:`linear.existing-connector`,message:`A Linear connector named "${i}" already exists. What would you like to do?`,options:[{id:`reuse`,label:`Reuse existing connector`,value:`reuse`},{id:`new`,label:`Create a new connector`,value:`new`},{id:`exit`,label:`Exit setup`,value:`exit`}],recommended:`reuse`}));if(r===`exit`)return;if(r===`reuse`)return await t.attachConnector({connector:a,log:e.ui.prompter.log,project:n,projectRoot:e.appRoot,signal:e.signal}),a}let o=a===void 0?i:linearSafeConnectorSlug(await e.ui.asker.ask(text({key:`linear.new-connector-name`,message:`Name the new Linear agent`,recommended:`${i}-2`,validate:e=>e.trim().length===0?`A Linear agent name is required.`:null})));return t.provisionConnector({log:e.ui.prompter.log,project:n,projectRoot:e.appRoot,slug:o,signal:e.signal})}async function setupLinear(e,t=defaultDeps){if(e.environment.vercel.kind===`unavailable`)throw Error("Linear setup requires an authenticated Vercel CLI. Run `vercel login`, then retry.");try{let n=await chooseConnector(e,t,await t.ensureVercelProject({appRoot:e.appRoot,prompter:e.ui.prompter,signal:e.signal}));return n===void 0?{kind:`cancelled`}:(await t.writeTextFile(join(e.appRoot,`agent/channels/linear.ts`),connectTemplate(n.uid),{force:e.force}),{kind:`done`,facts:[{label:`Vercel Connect`,value:`https://vercel.com/d?to=/%5Bteam%5D/~/connect&title=Open+Vercel+Connect`,kind:`url`},{label:`Next step`,value:`Deploy the agent, then open the Linear app in Vercel Connect and install it in the workspace where you want to delegate issues and comments.`},{label:`In Linear`,value:`Delegate an issue or mention the agent in an Agent Session to start a conversation.`}]})}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}const LINEAR_SETUP={kind:`linear`,label:`Linear Agent`,hint:`Delegate Linear issues and comments`,setup:setupLinear};export{LINEAR_SETUP,linearSafeConnectorSlug,setupLinear};
7
+ `}async function prepareLinearSetup(e,t=defaultDeps){if(e.environment.vercel.kind===`unavailable`)throw Error("Linear setup requires an authenticated Vercel CLI. Run `vercel login`, then retry.");let n=await e.resolveVercelProject(`Linear`),r=linearSafeConnectorSlug(await t.deriveConnectorSlug(e.appRoot)),i=linearSafeConnectorSlug(await e.asker.ask(text({key:`linear.connector-name`,message:`Name your Linear agent`,recommended:r,validate:e=>e.trim().length===0?`A Linear agent name is required.`:null}))),a=await t.findConnector({project:n,projectRoot:e.appRoot,slug:i,signal:e.signal});if(a===void 0)return{project:n,connector:{kind:`create`,slug:i}};let o=await e.asker.ask(select({key:`linear.existing-connector`,message:`A Linear connector named "${i}" already exists. What would you like to do?`,options:[{id:`reuse`,label:`Reuse existing connector`,value:`reuse`},{id:`new`,label:`Create a new connector`,value:`new`},{id:`exit`,label:`Exit setup`,value:`exit`}],recommended:`reuse`}));if(o===`exit`)throw new WizardCancelledError;return o===`reuse`?{project:n,connector:{kind:`reuse`,connector:a}}:{project:n,connector:{kind:`create`,slug:linearSafeConnectorSlug(await e.asker.ask(text({key:`linear.new-connector-name`,message:`Name the new Linear agent`,recommended:`${i}-2`,validate:e=>e.trim().length===0?`A Linear agent name is required.`:null})))}}}async function applyLinearSetup(e,t,n=defaultDeps){let r=e.connector.kind===`reuse`?(await n.attachConnector({connector:e.connector.connector,log:t.presenter.log,project:e.project,projectRoot:t.appRoot,signal:t.signal}),e.connector.connector):await n.provisionConnector({log:t.presenter.log,project:e.project,projectRoot:t.appRoot,slug:e.connector.slug,signal:t.signal});return await n.writeTextFile(join(t.appRoot,`agent/channels/linear.ts`),connectTemplate(r.uid),{force:t.force}),t.presenter.nextSteps([`Deploy the agent, then open the Linear app in Vercel Connect and install it in the workspace where you want to delegate issues and comments.`,`Delegate an issue or mention the agent in an Agent Session to start a conversation.`]),{facts:[],deploymentRequired:!0}}const LINEAR_SETUP=defineSetupIntegration({kind:`linear`,label:`Linear Agent`,hint:`Delegate Linear issues and comments`,prepare:prepareLinearSetup,apply:applyLinearSetup});export{LINEAR_SETUP,applyLinearSetup,linearSafeConnectorSlug,prepareLinearSetup};
@@ -21,13 +21,20 @@ export interface ProvisionPhotonProjectOptions {
21
21
  export interface UsePhotonProjectOptions {
22
22
  projectId: string;
23
23
  projectSecret: string;
24
- phoneNumber: string;
24
+ dedicatedLine?: string;
25
+ phoneNumber?: string;
25
26
  deps?: PhotonManagementDeps;
26
27
  }
27
28
  export interface PhotonManagementDeps {
28
29
  fetch: typeof fetch;
29
30
  delay(ms: number, signal?: AbortSignal): Promise<void>;
30
31
  }
32
+ /** Returns the least-loaded dedicated iMessage line, when the project has one. */
33
+ export declare function findDedicatedPhotonLine(input: {
34
+ projectId: string;
35
+ projectSecret: string;
36
+ deps?: Pick<PhotonManagementDeps, "fetch">;
37
+ }): Promise<string | undefined>;
31
38
  /** Authorizes Photon and creates an isolated iMessage project for one eve agent. */
32
39
  export declare function provisionPhotonProject(options: ProvisionPhotonProjectOptions): Promise<PhotonManagedProject>;
33
40
  /** Validates existing Photon credentials and registers the agent's iMessage user. */
@@ -1 +1 @@
1
- import{z}from"#compiled/zod/index.js";import{setTimeout}from"node:timers/promises";const PHOTON_DASHBOARD_HOST=`https://app.photon.codes`,PHOTON_DEVICE_CLIENT_ID=`photon-cli`,E164=/^\+[1-9]\d{6,14}$/;function validatePhotonPhoneNumber(e){let t=e.trim();return E164.test(t)?t.startsWith(`+1`)&&t.length!==12?`US and Canadian numbers must be +1 followed by exactly 10 digits`:null:`Use E.164 format: + followed by 7–15 digits, for example +15551234567`}const DeviceAuthorizationSchema=z.object({device_code:z.string().min(1),user_code:z.string().min(1),verification_uri_complete:z.string().url().optional(),verification_uri:z.string().url().optional(),expires_in:z.number().positive().optional(),interval:z.number().positive().optional()}),DeviceTokenSchema=z.object({access_token:z.string().min(1).optional(),accessToken:z.string().min(1).optional()}),ProjectSchema=z.object({id:z.string().min(1)}),ProjectSecretSchema=z.object({projectSecret:z.string().min(1)}),ErrorResponseSchema=z.object({error:z.unknown().optional(),message:z.unknown().optional()}),PhoneRegistrationSchema=z.object({assignedPhoneNumber:z.string().optional(),phoneNumber:z.string().optional(),data:z.object({assignedPhoneNumber:z.string().optional(),phoneNumber:z.string().optional(),user:z.object({assignedPhoneNumber:z.string().optional(),phoneNumber:z.string().optional()}).optional()}).optional(),user:z.object({assignedPhoneNumber:z.string().optional(),phoneNumber:z.string().optional()}).optional()}),defaultDeps={fetch,delay:(e,n)=>setTimeout(e,void 0,{signal:n})};function errorDetail(e,t){let n=ErrorResponseSchema.safeParse(e);return String(n.success?n.data.error??n.data.message??t:t)}function parsePhotonResponse(e,t,n){let r=e.safeParse(t);if(!r.success)throw Error(n);return r.data}async function json(e,t){let n=await e.json().catch(()=>void 0);if(!e.ok)throw Error(`Photon ${t} failed: ${errorDetail(n,e.statusText)}`);return n}function bearer(e){return{authorization:`Bearer ${e}`,"content-type":`application/json`}}function basic(e,t){return{authorization:`Basic ${Buffer.from(`${e}:${t}`).toString(`base64`)}`,"content-type":`application/json`}}async function requestDeviceCode(e){let t=await e.fetch(`${PHOTON_DASHBOARD_HOST}/api/auth/device/code`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({client_id:PHOTON_DEVICE_CLIENT_ID,scope:`openid profile email`})}),i=parsePhotonResponse(DeviceAuthorizationSchema,await json(t,`device login`),`Photon returned an invalid device authorization response.`),o=i.verification_uri_complete??i.verification_uri;if(o===void 0)throw Error(`Photon returned an invalid device authorization response.`);return{deviceCode:i.device_code,userCode:i.user_code,verificationUrl:o,expiresIn:i.expires_in??1800,interval:i.interval??5}}async function pollForToken(e,t,i){let a=Date.now()+e.expiresIn*1e3,s=e.interval*1e3;for(;Date.now()<a;){await t.delay(s,i);let a=await t.fetch(`${PHOTON_DASHBOARD_HOST}/api/auth/device/token`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:e.deviceCode,client_id:PHOTON_DEVICE_CLIENT_ID}),signal:i}),c=await a.json().catch(()=>void 0);if(a.ok){let e=parsePhotonResponse(DeviceTokenSchema,c,`Photon approved device login without returning an access token.`);if(e.access_token!==void 0)return e.access_token;if(e.accessToken!==void 0)return e.accessToken;throw Error(`Photon approved device login without returning an access token.`)}let l=ErrorResponseSchema.safeParse(c),u=l.success?l.data.error??l.data.message:void 0;if(u!==`authorization_pending`){if(u===`slow_down`||a.status===429){s+=a.status===429?1e4:5e3;continue}throw Error(`Photon device login failed: ${String(u??a.statusText)}`)}}throw Error(`Photon device login timed out.`)}async function createProject(e,t,r){let i=await r.fetch(`${PHOTON_DASHBOARD_HOST}/api/projects`,{method:`POST`,headers:bearer(e),body:JSON.stringify({name:t,location:`United States`,platforms:[`imessage`],template:!1,observability:!1})});return parsePhotonResponse(ProjectSchema,await json(i,`project creation`),`Photon did not return a project ID.`).id}async function regenerateSecret(e,t,r){let i=await r.fetch(`${PHOTON_DASHBOARD_HOST}/api/projects/${encodeURIComponent(t)}/regenerate-secret`,{method:`POST`,headers:bearer(e),body:`{}`});return parsePhotonResponse(ProjectSecretSchema,await json(i,`project credential provisioning`),`Photon did not return the new project secret.`).projectSecret}async function registerUser(e,t,n,r){let i=await r.fetch(`https://spectrum.photon.codes/projects/${encodeURIComponent(e)}/users/`,{method:`POST`,headers:basic(e,t),body:JSON.stringify({type:`shared`,phoneNumber:n})}),a=parsePhotonResponse(PhoneRegistrationSchema,await json(i,`phone registration`),`Photon returned an invalid phone registration response.`);return a.data?.user?.assignedPhoneNumber??a.data?.user?.phoneNumber??a.data?.assignedPhoneNumber??a.data?.phoneNumber??a.user?.assignedPhoneNumber??a.user?.phoneNumber??a.assignedPhoneNumber??a.phoneNumber}async function deleteProject(e,t,r){let i=await r.fetch(`${PHOTON_DASHBOARD_HOST}/api/projects/${encodeURIComponent(t)}`,{method:`DELETE`,headers:bearer(e)});!i.ok&&i.status!==404&&await json(i,`project cleanup`)}async function provisionPhotonProject(e){let t=e.phoneNumber.trim(),n=validatePhotonPhoneNumber(t);if(n!==null)throw Error(`Photon phone number is invalid. ${n}.`);let r=e.deps??defaultDeps,i=await requestDeviceCode(r);e.onAuthorization({userCode:i.userCode,verificationUrl:i.verificationUrl});let a=await pollForToken(i,r,e.signal),o=await createProject(a,e.projectName,r),cleanup=()=>deleteProject(a,o,r);try{let e=await regenerateSecret(a,o,r),n=await registerUser(o,e,t,r);return n===void 0?{projectId:o,projectSecret:e,cleanup}:{projectId:o,projectSecret:e,assignedPhoneNumber:n,cleanup}}catch(e){throw await cleanup().catch(()=>{}),e}}async function usePhotonProject(e){let t=e.phoneNumber.trim(),n=validatePhotonPhoneNumber(t);if(n!==null)throw Error(`Photon phone number is invalid. ${n}.`);let r=e.projectId.trim(),i=e.projectSecret.trim();if(!r||!i)throw Error(`Photon project ID and project secret are required.`);let a=await registerUser(r,i,t,e.deps??defaultDeps),cleanup=async()=>{};return a===void 0?{projectId:r,projectSecret:i,cleanup}:{projectId:r,projectSecret:i,assignedPhoneNumber:a,cleanup}}export{provisionPhotonProject,usePhotonProject,validatePhotonPhoneNumber};
1
+ import{z}from"#compiled/zod/index.js";import{setTimeout}from"node:timers/promises";const PHOTON_DASHBOARD_HOST=`https://app.photon.codes`,PHOTON_SPECTRUM_HOST=`https://spectrum.photon.codes`,PHOTON_DEVICE_CLIENT_ID=`photon-cli`,E164=/^\+[1-9]\d{6,14}$/;function validatePhotonPhoneNumber(e){let t=e.trim();return E164.test(t)?t.startsWith(`+1`)&&t.length!==12?`US and Canadian numbers must be +1 followed by exactly 10 digits`:null:`Use E.164 format: + followed by 7–15 digits, for example +15551234567`}const DeviceAuthorizationSchema=z.object({device_code:z.string().min(1),user_code:z.string().min(1),verification_uri_complete:z.string().url().optional(),verification_uri:z.string().url().optional(),expires_in:z.number().positive().optional(),interval:z.number().positive().optional()}),DeviceTokenSchema=z.object({access_token:z.string().min(1).optional(),accessToken:z.string().min(1).optional()}),ProjectSchema=z.object({id:z.string().min(1)}),ProjectSecretSchema=z.object({projectSecret:z.string().min(1)}),ErrorResponseSchema=z.object({error:z.unknown().optional(),message:z.unknown().optional()}),PhoneRegistrationSchema=z.object({data:z.object({assignedPhoneNumber:z.string().min(1)})}),DedicatedLineSchema=z.object({data:z.object({line:z.object({phoneNumber:z.string().min(1)})})}),defaultDeps={fetch,delay:(e,n)=>setTimeout(e,void 0,{signal:n})};function errorDetail(e,t){let n=ErrorResponseSchema.safeParse(e);return String(n.success?n.data.error??n.data.message??t:t)}function parsePhotonResponse(e,t,n){let r=e.safeParse(t);if(!r.success)throw Error(n);return r.data}async function json(e,t){let n=await e.json().catch(()=>void 0);if(!e.ok)throw Error(`Photon ${t} failed: ${errorDetail(n,e.statusText)}`);return n}function bearer(e){return{authorization:`Bearer ${e}`,"content-type":`application/json`}}function basic(e,t){return{authorization:`Basic ${Buffer.from(`${e}:${t}`).toString(`base64`)}`,"content-type":`application/json`}}async function requestDeviceCode(e){let t=await e.fetch(`${PHOTON_DASHBOARD_HOST}/api/auth/device/code`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({client_id:PHOTON_DEVICE_CLIENT_ID,scope:`openid profile email`})}),r=parsePhotonResponse(DeviceAuthorizationSchema,await json(t,`device login`),`Photon returned an invalid device authorization response.`),a=r.verification_uri_complete??r.verification_uri;if(a===void 0)throw Error(`Photon returned an invalid device authorization response.`);return{deviceCode:r.device_code,userCode:r.user_code,verificationUrl:a,expiresIn:r.expires_in??1800,interval:r.interval??5}}async function pollForToken(e,t,r){let a=Date.now()+e.expiresIn*1e3,o=e.interval*1e3;for(;Date.now()<a;){await t.delay(o,r);let a=await t.fetch(`${PHOTON_DASHBOARD_HOST}/api/auth/device/token`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:e.deviceCode,client_id:PHOTON_DEVICE_CLIENT_ID}),signal:r}),s=await a.json().catch(()=>void 0);if(a.ok){let e=parsePhotonResponse(DeviceTokenSchema,s,`Photon approved device login without returning an access token.`);if(e.access_token!==void 0)return e.access_token;if(e.accessToken!==void 0)return e.accessToken;throw Error(`Photon approved device login without returning an access token.`)}let c=ErrorResponseSchema.safeParse(s),l=c.success?c.data.error??c.data.message:void 0;if(l!==`authorization_pending`){if(l===`slow_down`||a.status===429){o+=a.status===429?1e4:5e3;continue}throw Error(`Photon device login failed: ${String(l??a.statusText)}`)}}throw Error(`Photon device login timed out.`)}async function createProject(e,t,r){let i=await r.fetch(`${PHOTON_DASHBOARD_HOST}/api/projects`,{method:`POST`,headers:bearer(e),body:JSON.stringify({name:t,location:`United States`,platforms:[`imessage`],template:!1,observability:!1})});return parsePhotonResponse(ProjectSchema,await json(i,`project creation`),`Photon did not return a project ID.`).id}async function regenerateSecret(e,t,r){let i=await r.fetch(`${PHOTON_DASHBOARD_HOST}/api/projects/${encodeURIComponent(t)}/regenerate-secret`,{method:`POST`,headers:bearer(e),body:`{}`});return parsePhotonResponse(ProjectSecretSchema,await json(i,`project credential provisioning`),`Photon did not return the new project secret.`).projectSecret}async function registerUser(e,t,n,i){let a=await i.fetch(`${PHOTON_SPECTRUM_HOST}/projects/${encodeURIComponent(e)}/users/`,{method:`POST`,headers:basic(e,t),body:JSON.stringify({type:`shared`,phoneNumber:n})});return parsePhotonResponse(PhoneRegistrationSchema,await json(a,`phone registration`),`Photon returned an invalid phone registration response.`).data.assignedPhoneNumber}async function findDedicatedPhotonLine(e){let t=await(e.deps?.fetch??fetch)(`${PHOTON_SPECTRUM_HOST}/projects/${encodeURIComponent(e.projectId)}/lines/route`,{headers:basic(e.projectId,e.projectSecret)});if(t.status!==404)return parsePhotonResponse(DedicatedLineSchema,await json(t,`dedicated line lookup`),`Photon returned an invalid dedicated line response.`).data.line.phoneNumber}async function deleteProject(e,t,r){let i=await r.fetch(`${PHOTON_DASHBOARD_HOST}/api/projects/${encodeURIComponent(t)}`,{method:`DELETE`,headers:bearer(e)});!i.ok&&i.status!==404&&await json(i,`project cleanup`)}async function provisionPhotonProject(e){let t=e.phoneNumber.trim(),n=validatePhotonPhoneNumber(t);if(n!==null)throw Error(`Photon phone number is invalid. ${n}.`);let r=e.deps??defaultDeps,i=await requestDeviceCode(r);e.onAuthorization({userCode:i.userCode,verificationUrl:i.verificationUrl});let a=await pollForToken(i,r,e.signal),o=await createProject(a,e.projectName,r),cleanup=()=>deleteProject(a,o,r);try{let e=await regenerateSecret(a,o,r),n=await registerUser(o,e,t,r);return n===void 0?{projectId:o,projectSecret:e,cleanup}:{projectId:o,projectSecret:e,assignedPhoneNumber:n,cleanup}}catch(e){throw await cleanup().catch(()=>{}),e}}async function usePhotonProject(e){let t=e.projectId.trim(),n=e.projectSecret.trim();if(!t||!n)throw Error(`Photon project ID and project secret are required.`);let r=e.dedicatedLine??await findDedicatedPhotonLine({projectId:t,projectSecret:n,deps:e.deps});if(r!==void 0)return{projectId:t,projectSecret:n,assignedPhoneNumber:r,cleanup:async()=>{}};if(e.phoneNumber===void 0)throw Error(`Photon phone number is required for projects without a dedicated line.`);let i=e.phoneNumber.trim(),a=validatePhotonPhoneNumber(i);if(a!==null)throw Error(`Photon phone number is invalid. ${a}.`);return{projectId:t,projectSecret:n,assignedPhoneNumber:await registerUser(t,n,i,e.deps??defaultDeps),cleanup:async()=>{}}}export{findDedicatedPhotonLine,provisionPhotonProject,usePhotonProject,validatePhotonPhoneNumber};
@@ -1,39 +1,43 @@
1
1
  import { appendEnv } from "../../append-env.js";
2
- import { provisionPhotonConnector } from "./connect.js";
3
- import { provisionPhotonProject, usePhotonProject } from "./management.js";
2
+ import type { VercelProjectReference } from "../../project-resolution.js";
4
3
  import { openUrl } from "../../primitives/open-url.js";
5
4
  import { deriveSlackConnectorSlug } from "../../scaffold/index.js";
6
5
  import { writeTextFile } from "../../scaffold/files.js";
7
- import type { IntegrationSetupEnvironment } from "../shared/environment.js";
8
- import type { IntegrationSetupUi } from "../shared/ui.js";
9
- import { ensureVercelProject } from "../../flows/ensure-vercel-project.js";
10
- /** Inputs for Photon project provisioning and channel scaffolding. */
11
- export interface PhotonSetupOptions {
6
+ import type { SetupApplyContext, SetupPrepareContext } from "../types.js";
7
+ import { provisionPhotonConnector } from "./connect.js";
8
+ import { findDedicatedPhotonLine, provisionPhotonProject, usePhotonProject } from "./management.js";
9
+ export interface PhotonSetupPlan {
12
10
  agentName: string;
13
- projectPath: string;
14
- environment: IntegrationSetupEnvironment;
15
- ui: IntegrationSetupUi;
16
- signal?: AbortSignal;
17
- force?: boolean;
18
- deps?: PhotonSetupDeps;
11
+ credentials: "vercel-connect" | "environment";
12
+ photonProject: "create" | {
13
+ projectId: string;
14
+ projectSecret: string;
15
+ };
16
+ photonProjectName?: string;
17
+ dedicatedLine?: string;
18
+ phoneNumber?: string;
19
+ vercelProject?: VercelProjectReference;
19
20
  }
20
- /** Outcome of Photon project provisioning and channel scaffolding. */
21
- export type PhotonSetupResult = {
22
- kind: "done";
23
- assignedPhoneNumber?: string;
24
- dashboardUrl: string;
25
- } | {
26
- kind: "cancelled";
27
- };
28
21
  export interface PhotonSetupDeps {
29
22
  appendEnv: typeof appendEnv;
30
23
  deriveConnectorSlug: typeof deriveSlackConnectorSlug;
31
- ensureVercelProject: typeof ensureVercelProject;
24
+ findDedicatedLine: typeof findDedicatedPhotonLine;
32
25
  openUrl: typeof openUrl;
33
26
  provisionConnector: typeof provisionPhotonConnector;
34
27
  provisionProject: typeof provisionPhotonProject;
35
28
  useProject: typeof usePhotonProject;
36
29
  writeTextFile: typeof writeTextFile;
37
30
  }
38
- /** Provisions a Photon project and scaffolds its iMessage channel. */
39
- export declare function setupPhoton(options: PhotonSetupOptions): Promise<PhotonSetupResult>;
31
+ export declare function preparePhotonSetup(context: SetupPrepareContext, deps?: PhotonSetupDeps): Promise<PhotonSetupPlan>;
32
+ export declare function applyPhotonSetup(plan: PhotonSetupPlan, context: SetupApplyContext, deps?: PhotonSetupDeps): Promise<{
33
+ facts: ({
34
+ label: string;
35
+ value: string;
36
+ kind: "url";
37
+ } | {
38
+ label: string;
39
+ value: string;
40
+ kind: "phone";
41
+ })[];
42
+ deploymentRequired: true;
43
+ }>;
@@ -1,10 +1,10 @@
1
- import{appendEnv}from"../../append-env.js";import{text}from"../../ask.js";import{writeTextFile}from"../../scaffold/files.js";import{deriveSlackConnectorSlug}from"../../scaffold/update/channels.js";import"../../scaffold/index.js";import{WizardCancelledError}from"../../step.js";import{ensureVercelProject}from"../../flows/ensure-vercel-project.js";import{provisionPhotonConnector}from"./connect.js";import{provisionPhotonProject,usePhotonProject,validatePhotonPhoneNumber}from"./management.js";import{openUrl}from"../../primitives/open-url.js";import{join}from"node:path";const defaultDeps={appendEnv,deriveConnectorSlug:deriveSlackConnectorSlug,ensureVercelProject,openUrl,provisionConnector:provisionPhotonConnector,provisionProject:provisionPhotonProject,useProject:usePhotonProject,writeTextFile};function connectTemplate(e){return`import { connectPhotonCredentials } from "@vercel/connect/eve";
1
+ import{appendEnv}from"../../append-env.js";import{select,text}from"../../ask.js";import{writeTextFile}from"../../scaffold/files.js";import{deriveSlackConnectorSlug}from"../../scaffold/update/channels.js";import"../../scaffold/index.js";import{provisionPhotonConnector}from"./connect.js";import{findDedicatedPhotonLine,provisionPhotonProject,usePhotonProject,validatePhotonPhoneNumber}from"./management.js";import{openUrl}from"../../primitives/open-url.js";import{basename,join}from"node:path";const defaultDeps={appendEnv,deriveConnectorSlug:deriveSlackConnectorSlug,findDedicatedLine:findDedicatedPhotonLine,openUrl,provisionConnector:provisionPhotonConnector,provisionProject:provisionPhotonProject,useProject:usePhotonProject,writeTextFile};function connectTemplate(e){return`import { connectPhotonCredentials } from "@vercel/connect/eve";
2
2
  import { photonIMessageChannel } from "eve/channels/photon";
3
3
 
4
4
  export default photonIMessageChannel({
5
5
  credentials: connectPhotonCredentials(${JSON.stringify(e)}),
6
6
  });
7
- `}async function choosePhotonProject(e){let n=`eve · ${e.agentName||`agent`}`,r=[{value:`create`,label:`Create a new Photon project`,hint:`Name: ${n}`},{value:`existing`,label:`Use an existing Photon project`,hint:`Enter its project credentials`}],i=e.ui.prompter.selectEditable?await e.ui.prompter.selectEditable({message:`Photon project`,options:r,initialValue:`create`,editable:{value:`create`,defaultValue:n,formatHint:e=>`Name: ${e}`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}}):void 0;if((i?.value??await e.ui.prompter.select({message:`Photon project`,options:r,initialValue:`create`}))===`existing`){let n=await e.ui.asker.ask(text({key:`photon-project-id`,message:`Photon project ID`,required:!0})),r=await e.ui.asker.ask(text({key:`photon-project-secret`,message:`Photon project secret`,required:!0,sensitive:!0}));return{photonProject:{projectId:n.trim(),projectSecret:r.trim()}}}return{photonProject:`create`,photonProjectName:i?.kind===`edited`?i.text.trim():n}}async function chooseSetupPlan(e){try{let t=await e.ui.prompter.select({message:`How would you like to configure Photon?`,options:[{value:`vercel`,label:`Set up Vercel Connect`,hint:e.environment.vercel.kind===`available`?`Link this project and configure Photon automatically`:`Log in to Vercel and link this project`},{value:`portable`,label:`Use portable credentials`,hint:`Configure the Photon webhook manually after deployment`}],initialValue:e.environment.vercel.kind===`available`?`vercel`:`portable`});if(t===`vercel`&&e.environment.vercel.kind===`unavailable`)throw Error("Vercel Connect requires an authenticated Vercel CLI. Run `vercel login`, then retry Photon setup.");let n=await choosePhotonProject(e);return{credentials:t===`vercel`?`vercel-connect`:`environment`,...n}}catch(e){if(e instanceof WizardCancelledError)return`cancelled`;throw e}}async function resolvePhotonProject(e,t,n,r){if(t.photonProject!==`create`)return r.useProject({...t.photonProject,phoneNumber:n});let i=e.ui.prompter.log.spinner?.(`Waiting for Photon approval…`,{kind:`external-action`,emphasis:`browser`});try{return await r.provisionProject({projectName:t.photonProjectName??`eve · ${e.agentName||`agent`}`,phoneNumber:n,signal:e.signal,onAuthorization(t){e.ui.prompter.log.message(`Authorize Photon: ${t.verificationUrl}`),e.ui.prompter.log.message(`Photon code: ${t.userCode}`),r.openUrl(t.verificationUrl)}})}finally{i?.stop()}}async function scaffoldPhoton(e,n,r){let i=await e.ui.asker.ask(text({key:`photon-phone-number`,message:`Your iMessage phone number`,placeholder:`+15551234567`,required:!0,validate:validatePhotonPhoneNumber})),a=e.projectPath,o=await resolvePhotonProject(e,n,i,r);try{let t=join(a,`agent/channels/photon.ts`);if(n.credentials===`vercel-connect`){let n=await r.deriveConnectorSlug(a,e.agentName),i=await r.ensureVercelProject({appRoot:a,prompter:e.ui.prompter,signal:e.signal}),s=await r.provisionConnector({credentials:o,log:e.ui.prompter.log,project:i,projectRoot:a,slug:n,signal:e.signal});await r.writeTextFile(t,connectTemplate(s.uid),{force:e.force})}else await r.appendEnv(join(a,`.env.local`),{IMESSAGE_PROJECT_ID:o.projectId,IMESSAGE_PROJECT_SECRET:o.projectSecret}),await r.writeTextFile(t,`import { photonIMessageChannel } from "eve/channels/photon";
7
+ `}async function preparePhotonSetup(e,r=defaultDeps){let i=basename(e.appRoot),a=await e.asker.ask(select({key:`photon-credentials`,message:`How would you like to configure Photon?`,options:[{id:`vercel`,value:`vercel-connect`,label:`Set up Vercel Connect`,hint:`Use a linked Vercel project`},{id:`portable`,value:`environment`,label:`Use portable credentials`,hint:`Configure the Photon webhook manually after deployment`}],recommended:e.environment.vercel.kind===`available`?`vercel-connect`:`environment`,required:!0}));if(a===`vercel-connect`&&e.environment.vercel.kind===`unavailable`)throw Error("Vercel Connect requires an authenticated Vercel CLI. Run `vercel login`, then retry Photon setup.");let o=`eve · ${i||`agent`}`,s=await e.asker.askEditable({key:`photon-project-source`,message:`Photon project`,options:[{id:`create`,value:`create`,label:`Create a new Photon project`,hint:`Name: ${o}`},{id:`existing`,value:`existing`,label:`Use an existing Photon project`,hint:`Enter its project credentials`}],recommended:`create`,required:!0,editable:{key:`photon-project-name`,value:`create`,label:`Name`,recommended:o,validate:e=>e.trim().length===0?`Project name cannot be empty.`:null}}),c=`create`;if(s.value===`existing`){let t=await e.asker.ask(text({key:`photon-project-id`,message:`Photon project ID`,required:!0})),r=await e.asker.ask(text({key:`photon-project-secret`,message:`Photon project secret`,required:!0,sensitive:!0,environment:`IMESSAGE_PROJECT_SECRET`}));c={projectId:t.trim(),projectSecret:r.trim()}}let l=c===`create`?void 0:await r.findDedicatedLine(c),u=l===void 0?await e.asker.ask(text({key:`photon-phone-number`,message:`Your iMessage phone number`,placeholder:`+15551234567`,required:!0,validate:validatePhotonPhoneNumber})):void 0,d=a===`vercel-connect`?await e.resolveVercelProject(`Photon`):void 0,f={agentName:i,credentials:a,photonProject:c,dedicatedLine:l,phoneNumber:u};return s.value===`create`&&(f.photonProjectName=s.text??o),d!==void 0&&(f.vercelProject=d),f}async function resolvePhotonProject(e,t,n){if(e.photonProject!==`create`)return n.useProject({...e.photonProject,dedicatedLine:e.dedicatedLine,phoneNumber:e.phoneNumber});if(e.phoneNumber===void 0)throw Error(`Photon phone number is required.`);let r=t.presenter.log.spinner?.(`Waiting for Photon approval…`,{kind:`external-action`,emphasis:`browser`}),i;try{let r=await n.provisionProject({projectName:e.photonProjectName??`eve · ${e.agentName||`agent`}`,phoneNumber:e.phoneNumber,signal:t.signal,onAuthorization(e){i=t.presenter.beginExternalAction({message:`Authorize Photon`,url:e.verificationUrl,userCode:e.userCode}),n.openUrl(e.verificationUrl)}});return i?.complete(),r}finally{r?.stop()}}async function applyPhotonSetup(e,t,n=defaultDeps){let r=await resolvePhotonProject(e,t,n);try{let i=join(t.appRoot,`agent/channels/photon.ts`);if(e.credentials===`vercel-connect`){let a=await n.deriveConnectorSlug(t.appRoot,e.agentName),o=await n.provisionConnector({credentials:r,log:t.presenter.log,project:e.vercelProject,projectRoot:t.appRoot,slug:a,signal:t.signal});await n.writeTextFile(i,connectTemplate(o.uid),{force:t.force})}else await n.appendEnv(join(t.appRoot,`.env.local`),{IMESSAGE_PROJECT_ID:r.projectId,IMESSAGE_PROJECT_SECRET:r.projectSecret}),await n.writeTextFile(i,`import { photonIMessageChannel } from "eve/channels/photon";
8
8
 
9
9
  async function photonCredentials() {
10
10
  const projectId = process.env.IMESSAGE_PROJECT_ID;
@@ -17,4 +17,4 @@ export default photonIMessageChannel({
17
17
  credentials: photonCredentials,
18
18
  webhookSecret: process.env.IMESSAGE_WEBHOOK_SECRET,
19
19
  });
20
- `,{force:e.force}),e.ui.nextSteps([`Deploy the agent, then create a Photon webhook pointing to https://<your-host>/eve/v1/photon.`,`Copy the webhook signing secret into IMESSAGE_WEBHOOK_SECRET, alongside IMESSAGE_PROJECT_ID and IMESSAGE_PROJECT_SECRET, in your host's encrypted environment variables.`]);e.ui.prompter.log.success(`Scaffolded channel: photon`),o.assignedPhoneNumber!==void 0&&e.ui.prompter.note(o.assignedPhoneNumber,`Text your agent`,{tone:`success`});let i=`https://app.photon.codes/dashboard/${o.projectId}`;return e.ui.prompter.note(i,`Photon project`,{tone:`success`}),o.assignedPhoneNumber===void 0?{dashboardUrl:i}:{assignedPhoneNumber:o.assignedPhoneNumber,dashboardUrl:i}}catch(e){throw await o.cleanup().catch(()=>{}),e}}async function setupPhoton(e){try{let t=await chooseSetupPlan(e);return t===`cancelled`?{kind:`cancelled`}:{kind:`done`,...await scaffoldPhoton(e,t,e.deps??defaultDeps)}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}export{setupPhoton};
20
+ `,{force:t.force}),t.presenter.nextSteps([`Deploy the agent, then create a Photon webhook pointing to https://<your-host>/eve/v1/photon.`,`Copy the webhook signing secret into IMESSAGE_WEBHOOK_SECRET, alongside IMESSAGE_PROJECT_ID and IMESSAGE_PROJECT_SECRET, in your host's encrypted environment variables.`]);t.presenter.log.success(`Scaffolded channel: photon`),r.assignedPhoneNumber!==void 0&&t.presenter.note(r.assignedPhoneNumber,`Text your agent`,{tone:`success`});let a=`https://app.photon.codes/dashboard/${r.projectId}`;return t.presenter.note(a,`Photon project`,{tone:`success`}),{facts:[{label:`Photon project dashboard`,value:a,kind:`url`},...r.assignedPhoneNumber===void 0?[]:[{label:`Agent phone number`,value:r.assignedPhoneNumber,kind:`phone`}]],deploymentRequired:!0}}catch(e){throw await r.cleanup().catch(()=>{}),e}}export{applyPhotonSetup,preparePhotonSetup};
@@ -1,3 +1 @@
1
- import type { SetupIntegration } from "../types.js";
2
- /** Photon project provisioning and iMessage channel scaffolding. */
3
- export declare const PHOTON_SETUP: SetupIntegration;
1
+ export declare const PHOTON_SETUP: import("../types.js").SetupIntegration;
@@ -1 +1 @@
1
- import{setupPhoton}from"./setup-flow.js";import{basename}from"node:path";const PHOTON_SETUP={kind:`photon`,label:`Photon`,hint:`Messages through Photon`,async setup(e){let t=await setupPhoton({agentName:basename(e.appRoot),projectPath:e.appRoot,environment:e.environment,ui:e.ui,signal:e.signal,force:e.force});return t.kind===`cancelled`?t:{kind:`done`,facts:[...t.assignedPhoneNumber===void 0?[]:[{label:`Text your agent`,value:t.assignedPhoneNumber,kind:`phone`}],{label:`Photon project`,value:t.dashboardUrl,kind:`url`}]}}};export{PHOTON_SETUP};
1
+ import{defineSetupIntegration}from"../types.js";import{applyPhotonSetup,preparePhotonSetup}from"./setup-flow.js";const PHOTON_SETUP=defineSetupIntegration({kind:`photon`,label:`Photon`,hint:`Messages through Photon`,prepare:preparePhotonSetup,apply:applyPhotonSetup});export{PHOTON_SETUP};
@@ -3,4 +3,4 @@ import type { SetupIntegration } from "./types.js";
3
3
  export declare const SETUP_INTEGRATIONS: readonly SetupIntegration[];
4
4
  /** Resolves one built-in setup integration by its registry setup name. */
5
5
  export declare function setupIntegration(kind: string): SetupIntegration;
6
- export { createIntegrationSetupUi } from "./shared/ui.js";
6
+ export { createSetupContexts, createSetupPresenter } from "./shared/ui.js";
@@ -1 +1 @@
1
- import{DISCORD_SETUP}from"./discord/setup.js";import{GITHUB_SETUP}from"./github/setup.js";import{LINEAR_SETUP}from"./linear/setup.js";import{PHOTON_SETUP}from"./photon/setup.js";import{SLACK_SETUP}from"./slack/setup.js";import{WEB_SETUP}from"./web/setup.js";import{createIntegrationSetupUi}from"./shared/ui.js";const SETUP_INTEGRATIONS=[WEB_SETUP,SLACK_SETUP,DISCORD_SETUP,GITHUB_SETUP,LINEAR_SETUP,PHOTON_SETUP];function setupIntegration(e){let t=SETUP_INTEGRATIONS.find(t=>t.kind===e);if(t===void 0)throw Error(`Integration setup "${e}" is not available in this version of eve. Upgrade eve and try again.`);return t}export{SETUP_INTEGRATIONS,createIntegrationSetupUi,setupIntegration};
1
+ import{DISCORD_SETUP}from"./discord/setup.js";import{GITHUB_SETUP}from"./github/setup.js";import{LINEAR_SETUP}from"./linear/setup.js";import{PHOTON_SETUP}from"./photon/setup.js";import{SLACK_SETUP}from"./slack/setup.js";import{WEB_SETUP}from"./web/setup.js";import{createSetupContexts,createSetupPresenter}from"./shared/ui.js";const SETUP_INTEGRATIONS=[WEB_SETUP,SLACK_SETUP,DISCORD_SETUP,GITHUB_SETUP,LINEAR_SETUP,PHOTON_SETUP];function setupIntegration(e){let t=SETUP_INTEGRATIONS.find(t=>t.kind===e);if(t===void 0)throw Error(`Integration setup "${e}" is not available in this version of eve. Upgrade eve and try again.`);return t}export{SETUP_INTEGRATIONS,createSetupContexts,createSetupPresenter,setupIntegration};
@@ -1,25 +1,28 @@
1
- import type { RegistrySetupFact } from "#setup/registry-setup-protocol.js";
2
- import { detectDeployment } from "#setup/project-resolution.js";
1
+ import { type Asker } from "#setup/ask.js";
2
+ import { detectDeployment, type VercelProjectReference } from "#setup/project-resolution.js";
3
3
  import type { Prompter } from "#setup/prompter.js";
4
4
  import { getVercelAuthStatus } from "#setup/vercel-project.js";
5
+ import type { IntegrationSetupResult, SetupExternalAction } from "./types.js";
5
6
  /** Inputs shared by every registry-owned integration setup flow. */
6
7
  export interface RunIntegrationSetupOptions {
7
8
  appRoot: string;
8
9
  prompter: Prompter;
10
+ /** Defaults to the interactive adapter; agent drivers inject an answer-backed asker. */
11
+ asker?: Asker;
9
12
  signal?: AbortSignal;
10
- yes?: boolean;
13
+ force?: boolean;
14
+ beginExternalAction?: (input: {
15
+ url: string;
16
+ userCode?: string;
17
+ message: string;
18
+ }) => SetupExternalAction;
19
+ resolveVercelProject?: SetupProjectResolver;
11
20
  }
12
21
  /** Effects shared by the built-in integration setup runner. */
22
+ export type SetupProjectResolver = (integration: string) => Promise<VercelProjectReference>;
13
23
  export interface IntegrationSetupRunnerDeps {
14
24
  detectDeployment: typeof detectDeployment;
15
25
  getVercelAuthStatus: typeof getVercelAuthStatus;
16
26
  }
17
- /** Outcome returned by one registry-owned integration setup flow. */
18
- export type IntegrationSetupOutcome = {
19
- kind: "cancelled";
20
- } | {
21
- kind: "done";
22
- facts?: readonly RegistrySetupFact[];
23
- };
24
27
  /** Runs one built-in integration setup flow selected by its registry setup name. */
25
- export declare function runIntegrationSetup(kind: string, options: RunIntegrationSetupOptions, deps?: IntegrationSetupRunnerDeps): Promise<IntegrationSetupOutcome>;
28
+ export declare function runIntegrationSetup(kind: string, options: RunIntegrationSetupOptions, deps?: IntegrationSetupRunnerDeps): Promise<IntegrationSetupResult>;
@@ -1 +1 @@
1
- import{createIntegrationSetupUi}from"./shared/ui.js";import{setupIntegration}from"./registry.js";import{describeIntegrationSetupEnvironment,integrationSetupEnvironment}from"./shared/environment.js";import{detectDeployment,projectResolutionFromDeployment}from"#setup/project-resolution.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";import{interactiveAsker}from"#setup/ask.js";const defaultDeps={detectDeployment,getVercelAuthStatus};async function runIntegrationSetup(e,t,n=defaultDeps){let r=setupIntegration(e);t.prompter.intro(`Set up ${r.label}`),t.prompter.log.message(`Checking Vercel setup...`);let[i,a]=await Promise.all([n.detectDeployment(t.appRoot,{signal:t.signal}),n.getVercelAuthStatus(t.appRoot,{signal:t.signal})]),o=integrationSetupEnvironment(a,projectResolutionFromDeployment(i));return t.prompter.log.info(describeIntegrationSetupEnvironment(o)),await r.setup({environment:o,appRoot:t.appRoot,ui:createIntegrationSetupUi({asker:interactiveAsker(t.prompter),prompter:t.prompter}),yes:t.yes,signal:t.signal})}export{runIntegrationSetup};
1
+ import{createSetupContexts}from"./shared/ui.js";import{setupIntegration}from"./registry.js";import{describeIntegrationSetupEnvironment,integrationSetupEnvironment}from"./shared/environment.js";import{resolveIntegrationVercelProject}from"./shared/vercel-project.js";import{detectDeployment,projectResolutionFromDeployment}from"#setup/project-resolution.js";import{interactiveAsker}from"#setup/ask.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";const defaultDeps={detectDeployment,getVercelAuthStatus};async function runIntegrationSetup(t,n,r=defaultDeps){let i=setupIntegration(t);n.prompter.intro(`Set up ${i.label}`),n.prompter.log.message(`Checking Vercel setup...`);let[a,o]=await Promise.all([r.detectDeployment(n.appRoot,{signal:n.signal}),r.getVercelAuthStatus(n.appRoot,{signal:n.signal})]),s=integrationSetupEnvironment(o,projectResolutionFromDeployment(a));return n.prompter.log.info(describeIntegrationSetupEnvironment(s)),i.run(createSetupContexts({appRoot:n.appRoot,asker:n.asker??interactiveAsker(n.prompter),environment:s,prompter:n.prompter,resolveVercelProject:n.resolveVercelProject??(e=>resolveIntegrationVercelProject({appRoot:n.appRoot,integration:e,signal:n.signal})),signal:n.signal,force:n.force,beginExternalAction:n.beginExternalAction}))}export{runIntegrationSetup};
@@ -0,0 +1,17 @@
1
+ export type SetupPrerequisite = {
2
+ kind: "command";
3
+ code: string;
4
+ message: string;
5
+ command: string;
6
+ } | {
7
+ kind: "environment";
8
+ code: string;
9
+ message: string;
10
+ variable: string;
11
+ sensitive: true;
12
+ };
13
+ /** A setup blocker whose prerequisite must be satisfied by the caller. */
14
+ export declare class SetupPrerequisiteRequired extends Error {
15
+ readonly prerequisite: SetupPrerequisite;
16
+ constructor(prerequisite: SetupPrerequisite);
17
+ }
@@ -0,0 +1 @@
1
+ var SetupPrerequisiteRequired=class extends Error{prerequisite;constructor(e){super(e.message),this.name=`SetupPrerequisiteRequired`,this.prerequisite=e}};export{SetupPrerequisiteRequired};
@@ -1,18 +1,25 @@
1
- import { type Asker } from "../../ask.js";
1
+ import type { Asker } from "../../ask.js";
2
2
  import type { Prompter } from "../../prompter.js";
3
- /** UI capabilities available to a channel-owned setup hook. */
4
- export interface IntegrationSetupUi {
5
- readonly asker: Asker;
6
- readonly prompter: Prompter;
7
- confirm(input: {
8
- key: string;
9
- message: string;
10
- recommended?: boolean;
11
- }): Promise<boolean>;
12
- nextSteps(lines: readonly string[]): void;
13
- }
14
- /** Adapts the shared setup asker and prompter to the channel hook UI. */
15
- export declare function createIntegrationSetupUi(input: {
3
+ import type { SetupApplyContext, SetupExternalAction, SetupPrepareContext, SetupPresenter } from "../types.js";
4
+ export declare function createSetupPresenter(prompter: Prompter, beginExternalAction?: (input: {
5
+ url: string;
6
+ userCode?: string;
7
+ message: string;
8
+ }) => SetupExternalAction): SetupPresenter;
9
+ export declare function createSetupContexts(input: {
10
+ appRoot: string;
16
11
  asker: Asker;
12
+ environment: SetupPrepareContext["environment"];
17
13
  prompter: Prompter;
18
- }): IntegrationSetupUi;
14
+ resolveVercelProject: SetupPrepareContext["resolveVercelProject"];
15
+ signal?: AbortSignal;
16
+ force?: boolean;
17
+ beginExternalAction?: (input: {
18
+ url: string;
19
+ userCode?: string;
20
+ message: string;
21
+ }) => SetupExternalAction;
22
+ }): {
23
+ prepare: SetupPrepareContext;
24
+ apply: SetupApplyContext;
25
+ };
@@ -1,2 +1,2 @@
1
- import{SkippedSignal,confirm}from"../../ask.js";function createIntegrationSetupUi(n){return{...n,async confirm(r){try{return await n.asker.ask(confirm(r))}catch(t){if(t instanceof SkippedSignal)return!1;throw t}},nextSteps(e){e.length!==0&&n.prompter.note(e.join(`
2
- `),`Next steps`,{tone:`success`})}}}export{createIntegrationSetupUi};
1
+ function createSetupPresenter(e,t){return{log:e.log,note:e.note.bind(e),nextSteps(t){t.length>0&&e.note(t.join(`
2
+ `),`Next steps`,{tone:`success`})},beginExternalAction(n){return t===void 0?(e.log.message(n.message),e.log.message(n.url),n.userCode!==void 0&&e.log.message(`Code: ${n.userCode}`),{complete(){}}):t(n)}}}function createSetupContexts(e){let t=createSetupPresenter(e.prompter,e.beginExternalAction),n={appRoot:e.appRoot,presenter:t};return e.signal!==void 0&&(n.signal=e.signal),e.force!==void 0&&(n.force=e.force),{prepare:{...n,asker:e.asker,environment:e.environment,resolveVercelProject:e.resolveVercelProject},apply:n}}export{createSetupContexts,createSetupPresenter};
@@ -0,0 +1,11 @@
1
+ import { readProjectLink, type VercelProjectReference } from "#setup/project-resolution.js";
2
+ export interface IntegrationVercelProjectDeps {
3
+ readProjectLink: typeof readProjectLink;
4
+ }
5
+ /** Requires the caller to link a Vercel project before integration setup begins applying. */
6
+ export declare function resolveIntegrationVercelProject(input: {
7
+ appRoot: string;
8
+ integration: string;
9
+ signal?: AbortSignal;
10
+ deps?: IntegrationVercelProjectDeps;
11
+ }): Promise<VercelProjectReference>;
@@ -0,0 +1 @@
1
+ import{SetupPrerequisiteRequired}from"./prerequisite.js";import{readProjectLink}from"#setup/project-resolution.js";const defaultDeps={readProjectLink};async function resolveIntegrationVercelProject(e){e.signal?.throwIfAborted();let t=await(e.deps??defaultDeps).readProjectLink(e.appRoot);if(t!==void 0)return t;throw new SetupPrerequisiteRequired({kind:`command`,code:`vercel-project-link`,message:`Vercel Connect setup requires a linked Vercel project. Run \`eve link\`, then retry ${e.integration} setup.`,command:`eve link`})}export{resolveIntegrationVercelProject};
@@ -1,16 +1,31 @@
1
- import { deriveSlackConnectorSlug, ensureChannel } from "#setup/scaffold/index.js";
2
- import { ensureVercelProject } from "#setup/flows/ensure-vercel-project.js";
3
- import { provisionSlackbot, reconcileSlackUid } from "#setup/slackbot.js";
4
- import type { IntegrationSetupContext, IntegrationSetupResult, SetupIntegration } from "../types.js";
5
- /** Effects used by Slack setup. */
1
+ import type { VercelProjectReference } from "#setup/project-resolution.js";
2
+ import { deriveSlackConnectorSlug, ensureChannel, type SlackConnectorSlug } from "#setup/scaffold/index.js";
3
+ import { inspectSlackbotConnectors, provisionSlackbot, reconcileSlackUid, type SlackConnectorSelection } from "#setup/slackbot.js";
4
+ import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
6
5
  export interface SlackSetupDeps {
7
6
  deriveSlackConnectorSlug: typeof deriveSlackConnectorSlug;
8
7
  ensureChannel: typeof ensureChannel;
9
- ensureVercelProject: typeof ensureVercelProject;
8
+ inspectConnectors: typeof inspectSlackbotConnectors;
10
9
  provisionSlackbot: typeof provisionSlackbot;
11
10
  reconcileSlackUid: typeof reconcileSlackUid;
12
11
  }
13
- /** Runs the Slack setup flow. Exported for direct integration tests. */
14
- export declare function setupSlack(context: IntegrationSetupContext, deps?: SlackSetupDeps): Promise<IntegrationSetupResult>;
15
- /** Slack setup registration. */
16
- export declare const SLACK_SETUP: SetupIntegration;
12
+ type SlackSetupPlan = {
13
+ credentials: "environment";
14
+ slug: SlackConnectorSlug;
15
+ } | {
16
+ credentials: "vercel-connect";
17
+ slug: SlackConnectorSlug;
18
+ project: VercelProjectReference;
19
+ connector: SlackConnectorSelection;
20
+ };
21
+ export declare function prepareSlackSetup(context: SetupPrepareContext, deps?: SlackSetupDeps): Promise<SlackSetupPlan>;
22
+ export declare function applySlackSetup(plan: SlackSetupPlan, context: SetupApplyContext, deps?: SlackSetupDeps): Promise<{
23
+ facts: {
24
+ label: string;
25
+ value: string;
26
+ kind: "url";
27
+ }[];
28
+ deploymentRequired: true;
29
+ }>;
30
+ export declare const SLACK_SETUP: import("../types.js").SetupIntegration;
31
+ export {};
@@ -1 +1 @@
1
- import{installScaffoldDependencies,reportOverwrittenFiles}from"../shared/scaffold.js";import{deriveSlackConnectorSlug,ensureChannel}from"#setup/scaffold/index.js";import{WizardCancelledError}from"#setup/step.js";import{ensureVercelProject}from"#setup/flows/ensure-vercel-project.js";import{provisionSlackbot,reconcileSlackUid}from"#setup/slackbot.js";function slackbotFailureCopy(e){switch(e.state){case`not-installed`:return{reason:`Slackbot is not connected to a Slack workspace. Slack channel was not added.`,followUp:"Re-run `eve add channel/slack` after the workspace install is complete."};case`cleanup-failed`:return{reason:`The abandoned Slack connector could not be removed. Slack channel was not added.`,followUp:`Resolve the cleanup warning above before trying again.`};case`connector-lookup-failed`:return{reason:`Existing Slack connectors could not be inspected. Slack channel was not added.`,followUp:"Restore Vercel CLI access, then re-run `eve add channel/slack`."};case`installation-check-failed`:return{reason:`Slack workspace installation could not be verified. Slack channel was not added.`,followUp:"Verify Vercel Connect is reachable, then re-run `eve add channel/slack`."};case`existing-not-installed`:return{reason:`The existing Slack connector is not connected to a Slack workspace. Slack channel was not added.`,followUp:`Resolve the existing connector warning above before trying again.`};case`detach-failed`:return{reason:`Slackbot provisioning could not replace the existing trigger destination. Slack channel was not added.`,followUp:"Run the `vercel connect detach` and `vercel connect attach` commands above."};case`attach-failed`:return{reason:`Slackbot provisioning did not attach this project. Slack channel was not added.`,followUp:"Finish event delivery with the `vercel connect attach` command above."};case`create-failed`:return{reason:`Slackbot creation failed.`,followUp:"Add it later with `eve add channel/slack`."}}}const defaultDeps={deriveSlackConnectorSlug,ensureChannel,ensureVercelProject,provisionSlackbot,reconcileSlackUid};async function chooseCredentials(e){if(e.yes)return`vercel-connect`;try{return await e.ui.prompter.select({message:`How would you like to configure Slack?`,options:[{value:`vercel`,label:`Set up Vercel Connect`,hint:`Sign in and link this project`},{value:`portable`,label:`Use portable credentials`,hint:`Read Slack tokens from environment variables`}]})===`portable`?`environment`:`vercel-connect`}catch(e){if(e instanceof WizardCancelledError)return`cancelled`;throw e}}async function provisionSlack(e,t,n){let r={selectConnector:async(t,n)=>{if(e.yes)return n??t[0];let r=await e.ui.prompter.select({message:`Which Slack app would you like to use?`,options:[...t.map(e=>{let t={value:e.uid,label:`Use ${e.uid}`};return e.uid===n?.uid&&(t.hint=`Matches this agent`),t}),{value:`create`,label:`Create a new Slack app`}],initialValue:n?.uid});return r===`create`?`create`:t.find(e=>e.uid===r)}};e.signal!==void 0&&(r.signal=e.signal),e.ui.prompter.awaitChoice!==void 0&&(r.awaitChoice=e.ui.prompter.awaitChoice);let a=await t.provisionSlackbot(e.ui.prompter.log,e.appRoot,n,void 0,r);if(e.signal?.throwIfAborted(),a.state===`cancelled`)throw new WizardCancelledError;if(a.state!==`attached`){let e=slackbotFailureCopy(a);throw Error(`${e.reason} ${e.followUp}`)}return a}async function setupSlack(n,r=defaultDeps){let i=await chooseCredentials(n);if(i===`cancelled`)return{kind:`cancelled`};if(i===`vercel-connect`&&n.environment.vercel.kind===`unavailable`)throw Error("Vercel Connect requires an authenticated Vercel CLI. Run `vercel login`, then retry Slack setup.");let a=await r.deriveSlackConnectorSlug(n.appRoot);if(i===`environment`){let e=await r.ensureChannel({projectRoot:n.appRoot,kind:`slack`,slackConnectorSlug:a,slackCredentials:`environment`,force:n.force,skipDependencyMutation:!0});return reportOverwrittenFiles(n.ui.prompter.log,e.filesOverwritten),n.ui.prompter.log.success(`Scaffolded channel: slack`),n.ui.nextSteps([`Set SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET in .env.local (listed in .env.example).`,`Configure your Slack app to send events to /eve/v1/slack on your public agent URL.`]),{kind:`done`}}if((await r.ensureVercelProject({appRoot:n.appRoot,prompter:n.ui.prompter,signal:n.signal})).projectId.length===0)throw Error(`Slack setup with Vercel Connect requires a linked Vercel project.`);let o=await provisionSlack(n,r,a),s=await r.ensureChannel({projectRoot:n.appRoot,kind:`slack`,slackConnectorUid:o.connectorUid,slackConnectorSlug:a,force:n.force,skipDependencyMutation:!0});if(reportOverwrittenFiles(n.ui.prompter.log,s.filesOverwritten),s.action===`skipped`&&!await r.reconcileSlackUid(n.ui.prompter.log,n.appRoot,o,`slack/${a}`))throw Error(`Slack connector UID update is required before deployment.`);return n.ui.prompter.log.success(`Scaffolded channel: slack`),await installScaffoldDependencies({changed:s.packageJsonUpdated.length>0,log:n.ui.prompter.log,projectPath:n.appRoot,signal:n.signal}),{kind:`done`}}const SLACK_SETUP={kind:`slack`,label:`Slack`,hint:`Slack app mentions and DMs`,setup:setupSlack};export{SLACK_SETUP,setupSlack};
1
+ import{defineSetupIntegration}from"../types.js";import{installScaffoldDependencies,reportOverwrittenFiles}from"../shared/scaffold.js";import{deriveSlackConnectorSlug,ensureChannel}from"#setup/scaffold/index.js";import{WizardCancelledError}from"#setup/step.js";import{select}from"#setup/ask.js";import{inspectSlackbotConnectors,provisionSlackbot,reconcileSlackUid}from"#setup/slackbot.js";import{slackMessageDeepLink}from"#setup/slack-connect.js";function slackbotFailureCopy(e){switch(e.state){case`not-installed`:return{reason:`Slackbot is not connected to a Slack workspace. Slack channel was not added.`,followUp:"Re-run `eve add channel/slack` after the workspace install is complete."};case`cleanup-failed`:return{reason:`The abandoned Slack connector could not be removed. Slack channel was not added.`,followUp:`Resolve the cleanup warning above before trying again.`};case`connector-lookup-failed`:return{reason:`Existing Slack connectors could not be inspected. Slack channel was not added.`,followUp:"Restore Vercel CLI access, then re-run `eve add channel/slack`."};case`installation-check-failed`:return{reason:`Slack workspace installation could not be verified. Slack channel was not added.`,followUp:"Verify Vercel Connect is reachable, then re-run `eve add channel/slack`."};case`existing-not-installed`:return{reason:`The existing Slack connector is not connected to a Slack workspace. Slack channel was not added.`,followUp:`Resolve the existing connector warning above before trying again.`};case`detach-failed`:return{reason:`Slackbot provisioning could not replace the existing trigger destination. Slack channel was not added.`,followUp:"Run the `vercel connect detach` and `vercel connect attach` commands above."};case`attach-failed`:return{reason:`Slackbot provisioning did not attach this project. Slack channel was not added.`,followUp:"Finish event delivery with the `vercel connect attach` command above."};case`create-failed`:return{reason:`Slackbot creation failed.`,followUp:"Add it later with `eve add channel/slack`."}}}const defaultDeps={deriveSlackConnectorSlug,ensureChannel,inspectConnectors:inspectSlackbotConnectors,provisionSlackbot,reconcileSlackUid};async function chooseConnector(e,t,n){return t.length===0?`create`:e.asker.ask(select({key:`slack-connector`,message:`Which Slack app would you like to use?`,options:[...t.map(e=>({id:e.uid,value:e,label:`Use ${e.uid}`,hint:e.uid===n?.uid?`Matches this agent`:void 0})),{id:`create`,value:`create`,label:`Create a new Slack app`}],recommended:n??t[0]??`create`,required:!0}))}async function prepareSlackSetup(e,t=defaultDeps){let n=await e.asker.ask(select({key:`slack-credentials`,message:`How would you like to configure Slack?`,options:[{id:`vercel`,value:`vercel-connect`,label:`Set up Vercel Connect`,hint:`Use a linked Vercel project`},{id:`portable`,value:`environment`,label:`Use portable credentials`,hint:`Read Slack tokens from environment variables`}],recommended:`vercel-connect`,required:!0})),r=await t.deriveSlackConnectorSlug(e.appRoot);if(n===`environment`)return{credentials:n,slug:r};if(e.environment.vercel.kind===`unavailable`)throw Error("Vercel Connect requires an authenticated Vercel CLI. Run `vercel login`, then retry Slack setup.");let i=await e.resolveVercelProject(`Slack`);if(i.projectId.length===0)throw Error(`Slack setup with Vercel Connect requires a linked Vercel project.`);let a=await t.inspectConnectors(e.presenter.log,e.appRoot,r,e.signal);return{credentials:n,slug:r,project:i,connector:a.state===`found`?await chooseConnector(e,a.connectors,a.preferred):`create`}}async function applySlackSetup(e,r,i=defaultDeps){if(e.credentials===`environment`){let t=await i.ensureChannel({projectRoot:r.appRoot,kind:`slack`,slackConnectorSlug:e.slug,slackCredentials:`environment`,force:r.force,skipDependencyMutation:!0});return reportOverwrittenFiles(r.presenter.log,t.filesOverwritten),r.presenter.log.success(`Scaffolded channel: slack`),r.presenter.nextSteps([`Set SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET in .env.local (listed in .env.example).`,`Configure your Slack app to send events to /eve/v1/slack on your public agent URL.`]),{facts:[],deploymentRequired:!0}}let o=await i.provisionSlackbot(r.presenter.log,r.appRoot,e.slug,void 0,{signal:r.signal,selectConnector:async()=>e.connector});if(r.signal?.throwIfAborted(),o.state===`cancelled`)throw new WizardCancelledError;if(o.state!==`attached`){let e=slackbotFailureCopy(o);throw Error(`${e.reason} ${e.followUp}`)}let s=await i.ensureChannel({projectRoot:r.appRoot,kind:`slack`,slackConnectorUid:o.connectorUid,slackConnectorSlug:e.slug,force:r.force,skipDependencyMutation:!0});if(reportOverwrittenFiles(r.presenter.log,s.filesOverwritten),s.action===`skipped`&&!await i.reconcileSlackUid(r.presenter.log,r.appRoot,o,`slack/${e.slug}`))throw Error(`Slack connector UID update is required before deployment.`);return r.presenter.log.success(`Scaffolded channel: slack`),await installScaffoldDependencies({changed:s.packageJsonUpdated.length>0,log:r.presenter.log,projectPath:r.appRoot,signal:r.signal}),{facts:o.chatUrl===void 0?[]:[{label:`Agent Slack DM`,value:slackMessageDeepLink(o.chatUrl),kind:`url`}],deploymentRequired:!0}}const SLACK_SETUP=defineSetupIntegration({kind:`slack`,label:`Slack`,hint:`Slack app mentions and DMs`,prepare:prepareSlackSetup,apply:applySlackSetup});export{SLACK_SETUP,applySlackSetup,prepareSlackSetup};
@@ -1,26 +1,56 @@
1
- import type { RegistrySetupFact } from "#setup/registry-setup-protocol.js";
1
+ import type { Asker } from "#setup/ask.js";
2
+ import type { Prompter } from "#setup/prompter.js";
3
+ import type { VercelProjectReference } from "#setup/project-resolution.js";
4
+ import type { RegistrySetupCompletion } from "#setup/registry-setup-protocol.js";
2
5
  import type { IntegrationSetupEnvironment } from "./shared/environment.js";
3
- import type { IntegrationSetupUi } from "./shared/ui.js";
4
- /** Inputs available to one registry-owned integration setup flow. */
5
- export interface IntegrationSetupContext {
6
+ export interface SetupExternalAction {
7
+ complete(): void;
8
+ }
9
+ export type SetupPresenter = Pick<Prompter, "log" | "note"> & {
10
+ nextSteps(lines: readonly string[]): void;
11
+ beginExternalAction(input: {
12
+ url: string;
13
+ userCode?: string;
14
+ message: string;
15
+ }): SetupExternalAction;
16
+ };
17
+ export interface SetupPrepareContext {
6
18
  readonly appRoot: string;
19
+ readonly asker: Asker;
7
20
  readonly environment: IntegrationSetupEnvironment;
8
- readonly ui: IntegrationSetupUi;
9
- readonly signal?: AbortSignal;
10
- readonly force?: boolean;
11
- readonly yes?: boolean;
21
+ readonly presenter: SetupPresenter;
22
+ readonly resolveVercelProject: (integration: string) => Promise<VercelProjectReference>;
23
+ signal?: AbortSignal;
24
+ force?: boolean;
25
+ }
26
+ export interface SetupApplyContext {
27
+ readonly appRoot: string;
28
+ readonly presenter: SetupPresenter;
29
+ signal?: AbortSignal;
30
+ force?: boolean;
12
31
  }
13
- /** Outcome from one registry-owned integration setup flow. */
14
32
  export type IntegrationSetupResult = {
15
33
  readonly kind: "done";
16
- readonly facts?: readonly RegistrySetupFact[];
34
+ readonly completion: RegistrySetupCompletion;
17
35
  } | {
18
36
  readonly kind: "cancelled";
19
37
  };
20
- /** One built-in registry-owned integration setup flow. */
21
- export interface SetupIntegration {
38
+ interface SetupIntegrationDefinition<Plan> {
22
39
  readonly kind: string;
23
40
  readonly label: string;
24
41
  readonly hint?: string;
25
- setup(context: IntegrationSetupContext): Promise<IntegrationSetupResult>;
42
+ prepare(context: SetupPrepareContext): Promise<Plan>;
43
+ apply(plan: Plan, context: SetupApplyContext): Promise<RegistrySetupCompletion>;
44
+ }
45
+ export interface SetupIntegration {
46
+ readonly kind: string;
47
+ readonly label: string;
48
+ hint?: string;
49
+ run(input: {
50
+ prepare: SetupPrepareContext;
51
+ apply: SetupApplyContext;
52
+ }): Promise<IntegrationSetupResult>;
26
53
  }
54
+ /** Captures an integration's private plan type behind one executable lifecycle. */
55
+ export declare function defineSetupIntegration<Plan>(definition: SetupIntegrationDefinition<Plan>): SetupIntegration;
56
+ export {};
@@ -1 +1 @@
1
- export{};
1
+ import{WizardCancelledError}from"#setup/step.js";function defineSetupIntegration(t){let n={kind:t.kind,label:t.label,async run(n){try{let e=await t.prepare(n.prepare);return{kind:`done`,completion:await t.apply(e,n.apply)}}catch(t){if(t instanceof WizardCancelledError)return{kind:`cancelled`};throw t}}};return t.hint!==void 0&&(n.hint=t.hint),n}export{defineSetupIntegration};
@@ -1,3 +1,22 @@
1
- import type { SetupIntegration } from "../types.js";
2
- /** Web Chat scaffolding. */
3
- export declare const WEB_SETUP: SetupIntegration;
1
+ import { detectPackageManager } from "#setup/package-manager.js";
2
+ import { ensureChannel } from "#setup/scaffold/index.js";
3
+ import { installScaffoldDependencies } from "../shared/scaffold.js";
4
+ import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
5
+ export interface WebSetupDeps {
6
+ detectPackageManager: typeof detectPackageManager;
7
+ ensureChannel: typeof ensureChannel;
8
+ installScaffoldDependencies: typeof installScaffoldDependencies;
9
+ }
10
+ export interface WebSetupPlan {
11
+ configureVercelServices: boolean;
12
+ packageManager: Awaited<ReturnType<typeof detectPackageManager>>["kind"];
13
+ }
14
+ export declare function prepareWebSetup(context: SetupPrepareContext, deps?: WebSetupDeps): Promise<WebSetupPlan>;
15
+ export declare function applyWebSetup(plan: WebSetupPlan, context: SetupApplyContext, deps?: WebSetupDeps): Promise<{
16
+ facts: never[];
17
+ deploymentRequired?: undefined;
18
+ } | {
19
+ facts: never[];
20
+ deploymentRequired: true;
21
+ }>;
22
+ export declare const WEB_SETUP: import("../types.js").SetupIntegration;
@@ -1 +1 @@
1
- import{installScaffoldDependencies,reportOverwrittenFiles}from"../shared/scaffold.js";import{ensureChannel}from"#setup/scaffold/index.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectPackageManager}from"#setup/package-manager.js";function reportCompetingNextConfigFiles(e,t){for(let n of t??[])e.warning(`Found competing Next.js config at ${n}; merge any needed settings into next.config.ts and remove it before starting the preview, or Next.js may ignore the generated eve rewrite.`)}const WEB_SETUP={kind:`web`,label:`Web Chat`,hint:`Browser-based chat interface`,async setup(r){r.ui.prompter.log.message(`Scaffolding Web Chat channel files...`);let i=await ensureChannel({projectRoot:r.appRoot,kind:`web`,packageManager:(await detectPackageManager(r.appRoot)).kind,configureVercelServices:r.environment.vercel.kind===`available`,force:r.force,skipDependencyMutation:!0});return reportOverwrittenFiles(r.ui.prompter.log,i.filesOverwritten),i.kind===`web`&&i.action!==`skipped`&&i.nodeEngineOverride!==void 0&&r.ui.prompter.log.warning(formatNodeEngineOverrideWarning(i.nodeEngineOverride)),reportCompetingNextConfigFiles(r.ui.prompter.log,`competingNextConfigFiles`in i?i.competingNextConfigFiles:void 0),i.action===`skipped`?(r.ui.prompter.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`),{kind:`done`}):(r.ui.prompter.log.success(`Scaffolded channel: web`),await installScaffoldDependencies({changed:i.packageJsonUpdated.length>0,log:r.ui.prompter.log,projectPath:r.appRoot,signal:r.signal}),{kind:`done`})}};export{WEB_SETUP};
1
+ import{defineSetupIntegration}from"../types.js";import{installScaffoldDependencies,reportOverwrittenFiles}from"../shared/scaffold.js";import{ensureChannel}from"#setup/scaffold/index.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectPackageManager}from"#setup/package-manager.js";function reportCompetingNextConfigFiles(e,t){for(let n of t??[])e.warning(`Found competing Next.js config at ${n}; merge any needed settings into next.config.ts and remove it before starting the preview, or Next.js may ignore the generated eve rewrite.`)}const defaultDeps={detectPackageManager,ensureChannel,installScaffoldDependencies};async function prepareWebSetup(e,t=defaultDeps){return{packageManager:(await t.detectPackageManager(e.appRoot)).kind,configureVercelServices:e.environment.vercel.kind===`available`}}async function applyWebSetup(e,t,r=defaultDeps){t.presenter.log.message(`Scaffolding Web Chat channel files...`);let i={projectRoot:t.appRoot,kind:`web`,packageManager:e.packageManager,configureVercelServices:e.configureVercelServices,force:t.force,skipDependencyMutation:!0},a=await r.ensureChannel(i);return reportOverwrittenFiles(t.presenter.log,a.filesOverwritten),a.kind===`web`&&a.action!==`skipped`&&a.nodeEngineOverride!==void 0&&t.presenter.log.warning(formatNodeEngineOverrideWarning(a.nodeEngineOverride)),reportCompetingNextConfigFiles(t.presenter.log,`competingNextConfigFiles`in a?a.competingNextConfigFiles:void 0),a.action===`skipped`?(t.presenter.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`),{facts:[]}):(t.presenter.log.success(`Scaffolded channel: web`),await r.installScaffoldDependencies({changed:a.packageJsonUpdated.length>0,log:t.presenter.log,projectPath:t.appRoot,signal:t.signal}),{facts:[],deploymentRequired:!0})}const WEB_SETUP=defineSetupIntegration({kind:`web`,label:`Web Chat`,hint:`Browser-based chat interface`,prepare:prepareWebSetup,apply:applyWebSetup});export{WEB_SETUP,applyWebSetup,prepareWebSetup};