eve 0.31.3 → 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 (205) hide show
  1. package/CHANGELOG.md +23 -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/packages/eve-catalog/src/index.js +1 -1
  87. package/dist/src/protocol/routes.d.ts +8 -6
  88. package/dist/src/protocol/routes.js +1 -1
  89. package/dist/src/public/channels/slack/attachments.js +1 -1
  90. package/dist/src/public/channels/slack/defaults.js +1 -1
  91. package/dist/src/public/channels/slack/hitl.d.ts +14 -1
  92. package/dist/src/public/channels/slack/hitl.js +2 -2
  93. package/dist/src/public/definitions/callback-context.d.ts +2 -2
  94. package/dist/src/public/definitions/sandbox.d.ts +1 -1
  95. package/dist/src/public/definitions/tool.d.ts +9 -9
  96. package/dist/src/public/sandbox/index.d.ts +1 -1
  97. package/dist/src/public/tools/web-search.d.ts +2 -2
  98. package/dist/src/runtime/connections/callback-route.d.ts +4 -5
  99. package/dist/src/runtime/connections/callback-route.js +1 -1
  100. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  101. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -2
  102. package/dist/src/runtime/framework-tools/todo.js +1 -1
  103. package/dist/src/runtime/input/types.d.ts +1 -4
  104. package/dist/src/sandbox/state.d.ts +1 -0
  105. package/dist/src/setup/ask-editable.d.ts +22 -0
  106. package/dist/src/setup/ask-editable.js +1 -0
  107. package/dist/src/setup/ask-signals.d.ts +23 -0
  108. package/dist/src/setup/ask-signals.js +1 -0
  109. package/dist/src/setup/ask.d.ts +24 -25
  110. package/dist/src/setup/ask.js +1 -1
  111. package/dist/src/setup/boxes/deploy-project.js +1 -1
  112. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  113. package/dist/src/setup/connection-connector.js +3 -2
  114. package/dist/src/setup/flows/registry-session.d.ts +33 -0
  115. package/dist/src/setup/flows/registry-session.js +1 -0
  116. package/dist/src/setup/flows/registry.d.ts +16 -0
  117. package/dist/src/setup/flows/registry.js +1 -1
  118. package/dist/src/setup/index.js +1 -1
  119. package/dist/src/setup/integrations/discord/setup.d.ts +19 -7
  120. package/dist/src/setup/integrations/discord/setup.js +4 -3
  121. package/dist/src/setup/integrations/github/setup.d.ts +36 -7
  122. package/dist/src/setup/integrations/github/setup.js +2 -2
  123. package/dist/src/setup/integrations/linear/setup.d.ts +21 -9
  124. package/dist/src/setup/integrations/linear/setup.js +2 -2
  125. package/dist/src/setup/integrations/photon/management.d.ts +8 -1
  126. package/dist/src/setup/integrations/photon/management.js +1 -1
  127. package/dist/src/setup/integrations/photon/setup-flow.d.ts +28 -24
  128. package/dist/src/setup/integrations/photon/setup-flow.js +3 -3
  129. package/dist/src/setup/integrations/photon/setup.d.ts +1 -3
  130. package/dist/src/setup/integrations/photon/setup.js +1 -1
  131. package/dist/src/setup/integrations/registry.d.ts +1 -1
  132. package/dist/src/setup/integrations/registry.js +1 -1
  133. package/dist/src/setup/integrations/runner.d.ts +14 -11
  134. package/dist/src/setup/integrations/runner.js +1 -1
  135. package/dist/src/setup/integrations/shared/prerequisite.d.ts +17 -0
  136. package/dist/src/setup/integrations/shared/prerequisite.js +1 -0
  137. package/dist/src/setup/integrations/shared/ui.d.ts +22 -15
  138. package/dist/src/setup/integrations/shared/ui.js +2 -2
  139. package/dist/src/setup/integrations/shared/vercel-project.d.ts +11 -0
  140. package/dist/src/setup/integrations/shared/vercel-project.js +1 -0
  141. package/dist/src/setup/integrations/slack/setup.d.ts +25 -10
  142. package/dist/src/setup/integrations/slack/setup.js +1 -1
  143. package/dist/src/setup/integrations/types.d.ts +43 -13
  144. package/dist/src/setup/integrations/types.js +1 -1
  145. package/dist/src/setup/integrations/web/setup.d.ts +22 -3
  146. package/dist/src/setup/integrations/web/setup.js +1 -1
  147. package/dist/src/setup/prompter.d.ts +8 -0
  148. package/dist/src/setup/question-options.d.ts +15 -0
  149. package/dist/src/setup/question-options.js +1 -0
  150. package/dist/src/setup/registry-setup-client.d.ts +3 -4
  151. package/dist/src/setup/registry-setup-client.js +2 -2
  152. package/dist/src/setup/registry-setup-completion.d.ts +3 -0
  153. package/dist/src/setup/registry-setup-completion.js +1 -0
  154. package/dist/src/setup/registry-setup-protocol.d.ts +22 -3
  155. package/dist/src/setup/registry-setup-protocol.js +1 -1
  156. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  157. package/dist/src/setup/scaffold/create/project.js +55 -3
  158. package/dist/src/setup/scaffold/update/channels.js +1 -1
  159. package/dist/src/setup/setup-question-wire.d.ts +48 -0
  160. package/dist/src/setup/setup-question-wire.js +1 -0
  161. package/dist/src/setup/slackbot.d.ts +5 -1
  162. package/dist/src/setup/slackbot.js +1 -1
  163. package/dist/src/setup/state.d.ts +2 -2
  164. package/dist/src/shared/sandbox-backend.d.ts +6 -0
  165. package/dist/src/shared/sandbox-session.d.ts +15 -0
  166. package/dist/src/shared/tool-definition.d.ts +2 -2
  167. package/dist/src/shared/tool-schema.d.ts +5 -4
  168. package/dist/src/shared/tool-schema.js +1 -1
  169. package/dist/src/svelte/index.js +1 -1
  170. package/dist/src/svelte/use-eve-agent.js +1 -1
  171. package/dist/src/tracing/agent-otel-provider.d.ts +5 -38
  172. package/dist/src/tracing/agent-otel-provider.js +2 -2
  173. package/dist/src/tracing/agent-span-id-generator.d.ts +15 -0
  174. package/dist/src/tracing/agent-span-id-generator.js +1 -0
  175. package/dist/src/tracing/agent-trace-context-store.d.ts +1 -1
  176. package/dist/src/tracing/agent-trace-context-store.js +1 -1
  177. package/dist/src/tracing/agent-trace-state.d.ts +41 -0
  178. package/dist/src/tracing/agent-trace-state.js +1 -0
  179. package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
  180. package/dist/src/vue/index.js +1 -1
  181. package/dist/src/vue/use-eve-agent.js +1 -1
  182. package/docs/channels/chat-sdk.mdx +1 -1
  183. package/docs/channels/github.mdx +1 -1
  184. package/docs/channels/linear.mdx +1 -1
  185. package/docs/channels/overview.mdx +2 -2
  186. package/docs/channels/slack.mdx +154 -40
  187. package/docs/concepts/default-harness.md +3 -3
  188. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  189. package/docs/connections/mcp.mdx +6 -0
  190. package/docs/guides/dev-tui.md +58 -142
  191. package/docs/guides/dynamic-workflows.md +2 -0
  192. package/docs/guides/frontend/overview.mdx +4 -2
  193. package/docs/guides/hooks.md +25 -3
  194. package/docs/guides/session-context.md +18 -0
  195. package/docs/install-integrations.mdx +2 -0
  196. package/docs/reference/cli.md +5 -3
  197. package/docs/reference/typescript-api.md +1 -1
  198. package/docs/sandbox.mdx +25 -5
  199. package/docs/skills.mdx +12 -1
  200. package/docs/subagents.mdx +1 -1
  201. package/docs/tools/human-in-the-loop.md +1 -1
  202. package/docs/tutorial/ship-it.mdx +1 -1
  203. package/package.json +10 -10
  204. package/dist/src/compiled/_chunks/node/undici-DWL_MYmT.js +0 -95
  205. package/dist/src/compiled/_chunks/workflow/dist-CZxNEvEs.js +0 -79
@@ -1,2 +1,3 @@
1
- import{readProjectLink}from"#setup/project-resolution.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";const CREATED_CONNECTOR=/\bConnector created:\s*(scl_[A-Za-z0-9_-]+)\b/u;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function parseJson(e){try{return JSON.parse(e)}catch{return}}function parseConnectorRef(e){if(!isRecord(e)||typeof e.uid!=`string`||typeof e.id!=`string`)return;let t={uid:e.uid,id:e.id};return typeof e.name==`string`&&(t.name=e.name),t}function parseCreatedConnector(e){let t=parseJson(e),n=parseConnectorRef(t);if(!isRecord(t)||n===void 0)return;let r=t.supportedSubjectTypes;return Array.isArray(r)&&r.includes(`user`)?n:void 0}function parseConnectorList(e,t){let n=[];for(let r of e){if(!isRecord(r)||typeof r.service==`string`&&r.service!==t)continue;let e=parseConnectorRef(r);e!==void 0&&n.push(e)}return n}function parseConnectorListPage(e,t){if(!isRecord(e)||!Array.isArray(e.connectors))return;let n=typeof e.cursor==`string`?e.cursor:void 0;return n===void 0?{connectors:parseConnectorList(e.connectors,t)}:{connectors:parseConnectorList(e.connectors,t),cursor:n}}async function listConnectors(e,n,r){let i=[],a=new Set,o;do{let s=[`connect`,`list`,`-F`,`json`,`--all-projects`,`--service`,e.service,`--scope`,n.orgId];o!==void 0&&s.push(`--next`,o);let c=await captureVercel(s,{cwd:e.projectRoot,onOutput:r,signal:e.signal});if(!c.ok)throw Error(c.failure.message);let l=parseConnectorListPage(parseJson(c.stdout),e.service);if(l===void 0)throw Error(`Vercel returned an invalid connector list for ${e.service}.`);i.push(...l.connectors);let u=l.cursor;if(u!==void 0&&a.has(u))throw Error(`The connector list repeated cursor ${u}.`);u!==void 0&&a.add(u),o=u}while(o!==void 0);return i}async function supportsUserAuthorization(e,n,r,i){let a=await captureVercel([`api`,`/v1/connect/connectors/${encodeURIComponent(r.id)}`,`--scope`,n.orgId,`--raw`],{cwd:e.projectRoot,onOutput:i,signal:e.signal});if(!a.ok)throw Error(`Could not verify connector ${r.uid}.`);let o=parseJson(a.stdout);if(!isRecord(o)||o.id!==r.id||o.uid!==r.uid||typeof o.service==`string`&&o.service!==e.service)throw Error(`Vercel returned invalid details for connector ${r.uid}.`);let s=o.supportedSubjectTypes;return Array.isArray(s)&&s.includes(`user`)}function connectorNames(e){let t=new Set;for(let n of e){n.name!==void 0&&t.add(n.name.toLowerCase());let e=n.uid.slice(n.uid.lastIndexOf(`/`)+1).trim();e.length>0&&t.add(e.toLowerCase())}return t}function connectorMatchesCanonicalName(e,t){let n=t.toLowerCase();return e.name?.toLowerCase()===n||e.uid.slice(e.uid.lastIndexOf(`/`)+1).trim().toLowerCase()===n}function nextConnectorName(e,t){if(!t.has(e.toLowerCase()))return e;let n=2;for(;t.has(`${e}-${n}`.toLowerCase());)n+=1;return`${e}-${n}`}async function cleanupCreatedConnectionConnector(t){let r=t.orgId??(await readProjectLink(t.projectRoot))?.orgId,a=[`connect`,`remove`,t.connectorId,`--disconnect-all`,`--yes`];if(r!==void 0&&a.push(`--scope`,r),!await runVercel(a,{cwd:t.projectRoot,onOutput:createPromptCommandOutput(t.log)}))throw Error(`Could not remove connector ${t.connectorId}; run \`vercel connect remove ${t.connectorId} --disconnect-all --yes\`.`)}async function attach(e,t,r,i){return runVercel([`connect`,`attach`,r,`--yes`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:i,signal:e.signal})}async function resolveFallbackConnector(e,t,n,i,s){let c=s;for(;;){if(await e.prompter.select({message:`Which connector should ${e.slug} use?`,hintLayout:`inline`,notices:[{tone:`warning`,text:c}],options:[{value:`find`,label:`Find a new one`,hint:`Browse existing connectors`},{value:`create`,label:`Create a new one`,hint:`Register another connector`}]})===`find`){let r=[];for(let a of i)await supportsUserAuthorization(e,t,a,n)&&r.push(a);if(r.length===0){c=`No existing ${e.service} connectors support user authorization.`;continue}let a=new Map(r.map(e=>[e.uid,e])),o=await e.prompter.select({message:`Select a connector for ${e.slug}`,hintLayout:`inline`,search:!0,placeholder:`type to search connectors`,options:r.map(e=>({value:e.uid,label:e.uid,hint:e.name??e.id}))}),s=a.get(o);if(s===void 0)throw Error(`Connector ${o} is no longer available.`);return{kind:`existing`,connector:s}}let s=connectorNames(i),l=(await e.prompter.text({message:`New connector name`,defaultValue:nextConnectorName(e.slug,s),validate:e=>{let t=e.trim().toLowerCase();return t.length===0?`A name is required.`:s.has(t)?`A connector with this name already exists.`:void 0}})).trim(),u=[],createOutput=e=>{u.push(e.text),n(e)},d=await withPhase(e.log,`Waiting for you to complete setup in the browser…`,()=>runVercelCaptureStdout([`connect`,`create`,e.service,`--name`,l,`-F`,`json`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:createOutput,signal:e.signal}),{kind:`external-action`,emphasis:`browser`}),f=parseConnectorRef(parseJson(d.stdout))?.id??CREATED_CONNECTOR.exec(u.join(`
2
- `))?.[1],p=d.ok?parseCreatedConnector(d.stdout):void 0;if(p!==void 0)return{kind:`created`,connector:p};let m=d.ok?`The ${e.service} connector does not support user authorization.`:`Could not create the ${e.service} connector.`;if(f!==void 0){try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:f,orgId:t.orgId})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`${m} ${t}`)}e.signal?.throwIfAborted()}throw Error(m)}}async function setupConnectionConnector(e){let t=createPromptCommandOutput(e.log),n=e.project,r=await listConnectors(e,n,t),a=r.find(t=>connectorMatchesCanonicalName(t,e.canonicalConnectorName)),o=`Could not find a connector named ${e.canonicalConnectorName}.`;if(a!==void 0)if(await supportsUserAuthorization(e,n,a,t)){if(await attach(e,n,a.uid,t))return e.log.success(`Attached ${a.uid} connector`),{kind:`existing`,connectorUid:a.uid};e.signal?.throwIfAborted(),o=`Could not attach ${a.uid}.`}else o=`${a.uid} does not support user authorization.`;let s=await resolveFallbackConnector(e,n,t,r,o);if(!await attach(e,n,s.connector.uid,t)){if(s.kind===`created`){let t=`Could not attach ${s.connector.uid} to the linked project.`;try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:s.connector.id,orgId:n.orgId})}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`${t} ${n}`)}throw e.signal?.throwIfAborted(),Error(t)}throw Error(`Could not attach ${s.connector.uid} to the linked project.`)}return e.log.success(`Attached ${s.connector.uid} connector`),s.kind===`created`?{kind:`created`,connectorUid:s.connector.uid,connectorId:s.connector.id}:{kind:`existing`,connectorUid:s.connector.uid}}export{cleanupCreatedConnectionConnector,parseCreatedConnector,setupConnectionConnector};
1
+ import{readProjectLink}from"#setup/project-resolution.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";const CREATED_CONNECTOR=/\bConnector created:\s*(scl_[A-Za-z0-9_-]+)\b/u;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function parseJson(e){try{return JSON.parse(e)}catch{return}}function connectorCreationFailure(e,t){let n=t?.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith(`Vercel CLI `)).at(-1);return n===void 0?`Could not create the ${e} connector.`:`Could not create the ${e} connector. Vercel returned: ${n}`}function parseConnectorRef(e){if(!isRecord(e)||typeof e.uid!=`string`||typeof e.id!=`string`)return;let t={uid:e.uid,id:e.id};return typeof e.name==`string`&&(t.name=e.name),t}function parseCreatedConnector(e){let t=parseJson(e),n=parseConnectorRef(t);if(!isRecord(t)||n===void 0)return;let r=t.supportedSubjectTypes;return Array.isArray(r)&&r.includes(`user`)?n:void 0}function parseConnectorList(e,t){let n=[];for(let r of e){if(!isRecord(r)||typeof r.service==`string`&&r.service!==t)continue;let e=parseConnectorRef(r);e!==void 0&&n.push(e)}return n}function parseConnectorListPage(e,t){if(!isRecord(e)||!Array.isArray(e.connectors))return;let n=typeof e.cursor==`string`?e.cursor:void 0;return n===void 0?{connectors:parseConnectorList(e.connectors,t)}:{connectors:parseConnectorList(e.connectors,t),cursor:n}}async function listConnectors(e,n,r){let i=[],a=new Set,o;do{let s=[`connect`,`list`,`-F`,`json`,`--all-projects`,`--service`,e.service,`--scope`,n.orgId];o!==void 0&&s.push(`--next`,o);let c=await captureVercel(s,{cwd:e.projectRoot,onOutput:r,signal:e.signal});if(!c.ok)throw Error(c.failure.message);let l=parseConnectorListPage(parseJson(c.stdout),e.service);if(l===void 0)throw Error(`Vercel returned an invalid connector list for ${e.service}.`);i.push(...l.connectors);let u=l.cursor;if(u!==void 0&&a.has(u))throw Error(`The connector list repeated cursor ${u}.`);u!==void 0&&a.add(u),o=u}while(o!==void 0);return i}async function supportsUserAuthorization(e,n,r,i){let a=await captureVercel([`api`,`/v1/connect/connectors/${encodeURIComponent(r.id)}`,`--scope`,n.orgId,`--raw`],{cwd:e.projectRoot,onOutput:i,signal:e.signal});if(!a.ok)throw Error(`Could not verify connector ${r.uid}.`);let o=parseJson(a.stdout);if(!isRecord(o)||o.id!==r.id||o.uid!==r.uid||typeof o.service==`string`&&o.service!==e.service)throw Error(`Vercel returned invalid details for connector ${r.uid}.`);let s=o.supportedSubjectTypes;return Array.isArray(s)&&s.includes(`user`)}function connectorNames(e){let t=new Set;for(let n of e){n.name!==void 0&&t.add(n.name.toLowerCase());let e=n.uid.slice(n.uid.lastIndexOf(`/`)+1).trim();e.length>0&&t.add(e.toLowerCase())}return t}function connectorMatchesCanonicalName(e,t){let n=t.toLowerCase();return e.name?.toLowerCase()===n||e.uid.slice(e.uid.lastIndexOf(`/`)+1).trim().toLowerCase()===n}function nextConnectorName(e,t){if(!t.has(e.toLowerCase()))return e;let n=2;for(;t.has(`${e}-${n}`.toLowerCase());)n+=1;return`${e}-${n}`}async function cleanupCreatedConnectionConnector(t){let r=t.orgId??(await readProjectLink(t.projectRoot))?.orgId,a=[`connect`,`remove`,t.connectorId,`--disconnect-all`,`--yes`];if(r!==void 0&&a.push(`--scope`,r),!await runVercel(a,{cwd:t.projectRoot,onOutput:createPromptCommandOutput(t.log)}))throw Error(`Could not remove connector ${t.connectorId}; run \`vercel connect remove ${t.connectorId} --disconnect-all --yes\`.`)}async function attach(e,t,r,i){return runVercel([`connect`,`attach`,r,`--yes`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:i,signal:e.signal})}async function resolveFallbackConnector(e,t,n,i,s){let c=s;for(;;){if(await e.prompter.select({message:`Which connector should ${e.slug} use?`,hintLayout:`inline`,notices:[{tone:`warning`,text:c}],options:[{value:`find`,label:`Find a new one`,hint:`Browse existing connectors`},{value:`create`,label:`Create a new one`,hint:`Register another connector`}]})===`find`){let r=[];for(let a of i)await supportsUserAuthorization(e,t,a,n)&&r.push(a);if(r.length===0){c=`No existing ${e.service} connectors support user authorization.`;continue}let a=new Map(r.map(e=>[e.uid,e])),o=await e.prompter.select({message:`Select a connector for ${e.slug}`,hintLayout:`inline`,search:!0,placeholder:`type to search connectors`,options:r.map(e=>({value:e.uid,label:e.uid,hint:e.name??e.id}))}),s=a.get(o);if(s===void 0)throw Error(`Connector ${o} is no longer available.`);return{kind:`existing`,connector:s}}let s=connectorNames(i),l=(await e.prompter.text({message:`New connector name`,defaultValue:nextConnectorName(e.slug,s),validate:e=>{let t=e.trim().toLowerCase();return t.length===0?`A name is required.`:s.has(t)?`A connector with this name already exists.`:void 0}})).trim(),u=[],createOutput=e=>{u.push(e.text),n(e)},d=await withPhase(e.log,`Waiting for you to complete setup in the browser…`,()=>runVercelCaptureStdout([`connect`,`create`,e.service,`--name`,l,`-F`,`json`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:createOutput,signal:e.signal}),{kind:`external-action`,emphasis:`browser`}),f=parseConnectorRef(parseJson(d.stdout))?.id??CREATED_CONNECTOR.exec(u.join(`
3
+ `))?.[1],p=d.ok?parseCreatedConnector(d.stdout):void 0;if(p!==void 0)return{kind:`created`,connector:p};let m=d.ok?`The ${e.service} connector does not support user authorization.`:connectorCreationFailure(e.service,d.stderr);if(f!==void 0){try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:f,orgId:t.orgId})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`${m} ${t}`)}e.signal?.throwIfAborted()}throw Error(m)}}async function setupConnectionConnector(e){let t=createPromptCommandOutput(e.log),n=e.project,r=await listConnectors(e,n,t),a=r.find(t=>connectorMatchesCanonicalName(t,e.canonicalConnectorName)),o=`Could not find a connector named ${e.canonicalConnectorName}.`;if(a!==void 0)if(await supportsUserAuthorization(e,n,a,t)){if(await attach(e,n,a.uid,t))return e.log.success(`Attached ${a.uid} connector`),{kind:`existing`,connectorUid:a.uid};e.signal?.throwIfAborted(),o=`Could not attach ${a.uid}.`}else o=`${a.uid} does not support user authorization.`;let s=await resolveFallbackConnector(e,n,t,r,o);if(!await attach(e,n,s.connector.uid,t)){if(s.kind===`created`){let t=`Could not attach ${s.connector.uid} to the linked project.`;try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:s.connector.id,orgId:n.orgId})}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`${t} ${n}`)}throw e.signal?.throwIfAborted(),Error(t)}throw Error(`Could not attach ${s.connector.uid} to the linked project.`)}return e.log.success(`Attached ${s.connector.uid} connector`),s.kind===`created`?{kind:`created`,connectorUid:s.connector.uid,connectorId:s.connector.id}:{kind:`existing`,connectorUid:s.connector.uid}}export{cleanupCreatedConnectionConnector,parseCreatedConnector,setupConnectionConnector};
@@ -0,0 +1,33 @@
1
+ import type { Prompter } from "#setup/prompter.js";
2
+ import { detectDeployment } from "#setup/project-resolution.js";
3
+ import type { RegistrySetupCompletion, RegistrySetupFact } from "#setup/registry-setup-protocol.js";
4
+ import { runDeployFlow } from "./deploy.js";
5
+ export interface RegistrySessionDeps {
6
+ detectDeployment: typeof detectDeployment;
7
+ runDeployFlow: typeof runDeployFlow;
8
+ }
9
+ export interface RegistrySessionItemResult {
10
+ address: string;
11
+ title: string;
12
+ facts: readonly RegistrySetupFact[];
13
+ output: readonly string[];
14
+ }
15
+ export interface RegistrySessionResult {
16
+ kind: "done";
17
+ addedItems: readonly string[];
18
+ items: readonly RegistrySessionItemResult[];
19
+ facts: readonly RegistrySetupFact[];
20
+ output: readonly string[];
21
+ deployed?: "production";
22
+ }
23
+ export interface RegistrySession {
24
+ add(item: string, title: string, output: readonly string[], setup?: RegistrySetupCompletion): void;
25
+ result(deployed?: "production"): RegistrySessionResult;
26
+ continueAfterInstall(input: {
27
+ appRoot: string;
28
+ prompter: Prompter;
29
+ signal?: AbortSignal;
30
+ }): Promise<"add-more" | RegistrySessionResult>;
31
+ }
32
+ /** Owns the accumulated output and deployment decision for one `/add` session. */
33
+ export declare function createRegistrySession(deps: RegistrySessionDeps): RegistrySession;
@@ -0,0 +1 @@
1
+ import"./deploy.js";import"#setup/project-resolution.js";import{mergeRegistrySetupCompletions}from"#setup/registry-setup-completion.js";function createRegistrySession(t){let n=[],r=[],i=[],a={facts:[]},o=``,s=[];function result(e){let t={kind:`done`,addedItems:n,items:r,facts:a.facts,output:i};return e!==void 0&&(t.deployed=e),t}return{add(t,c,l,u={facts:[]}){n.push(t),r.push({address:t,title:c,facts:u.facts,output:l}),i.push(...l),o=c,s=u.facts,a=mergeRegistrySetupCompletions(a,u)},result,async continueAfterInstall(e){if(a.deploymentRequired!==!0)return result();let n=await t.detectDeployment(e.appRoot,{signal:e.signal}),r=n.state===`linked`||n.state===`deployed`;for(e.prompter.replaceContent?.({headline:`Added ${o}`,facts:s});;){let n=await e.prompter.select({message:`What would you like to do next?`,initialValue:`add-more`,hintLayout:`inline`,options:[{value:`add-more`,label:`Add more`},...r?[{value:`deploy`,label:`Deploy`}]:[],{value:`finish`,label:`Finish`}]});if(n===`add-more`)return e.prompter.replaceContent?.(),`add-more`;if(n===`finish`)return result();if(await e.prompter.select({message:`Deploy to prod?`,initialValue:`yes`,options:[{value:`yes`,label:`Yes`},{value:`back`,label:`Back`}]})!==`back`)return result((await t.runDeployFlow({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal,interactive:!0})).kind===`deployed`?`production`:void 0)}}}}export{createRegistrySession};
@@ -1,16 +1,32 @@
1
1
  import type { Prompter } from "#setup/prompter.js";
2
+ import { runDeployFlow } from "#setup/flows/deploy.js";
3
+ import { detectDeployment } from "#setup/project-resolution.js";
4
+ import type { RegistrySetupFact } from "#setup/registry-setup-protocol.js";
2
5
  export interface RegistryFlowDeps {
3
6
  browseRegistryCatalog: (typeof import("#cli/commands/registry.js"))["browseRegistryCatalog"];
7
+ detectDeployment: typeof detectDeployment;
4
8
  getRegistryItemManifest: (typeof import("#cli/commands/registry.js"))["getRegistryItemManifest"];
5
9
  installRegistryItem: (typeof import("#cli/commands/registry.js"))["installRegistryItem"];
10
+ runDeployFlow: typeof runDeployFlow;
6
11
  }
7
12
  export type RegistryFlowResult = {
8
13
  kind: "done";
9
14
  addedItems: readonly string[];
15
+ items: readonly import("./registry-session.js").RegistrySessionItemResult[];
16
+ facts: readonly RegistrySetupFact[];
10
17
  output?: readonly string[];
18
+ deployed?: "production";
11
19
  } | {
12
20
  kind: "cancelled";
13
21
  };
22
+ export declare class RegistryFlowFailedError extends Error {
23
+ readonly completed: Extract<RegistryFlowResult, {
24
+ kind: "done";
25
+ }>;
26
+ constructor(error: unknown, completed: Extract<RegistryFlowResult, {
27
+ kind: "done";
28
+ }>);
29
+ }
14
30
  /** Runs the categorized interactive registry catalog used by the dev TUI's `/add`. */
15
31
  export declare function runRegistryFlow(input: {
16
32
  appRoot: string;
@@ -1 +1 @@
1
- import{WizardCancelledError}from"#setup/step.js";import{withSpinner}from"#setup/with-spinner.js";const ADDRESS_PREFIX=`address:`,BACK=`action:back`,DONE=`action:done`,ALL=`category:all`,REGISTRY_CATEGORIES=[{value:`category:channel`,prefix:`channel/`,label:`Channels`,hint:`Where people talk to your agent — Web, Slack, Discord, Teams`,browseLabel:`Browse channels`},{value:`category:connection`,prefix:`connection/`,label:`MCP connections`,hint:`Connect services like Linear, Notion, GitHub, and Vercel`,browseLabel:`Browse MCP connections`},{value:`category:extension`,prefix:`extension/`,label:`Extensions`,hint:`Add browser automation, memory, and developer tools`,browseLabel:`Browse extensions`},{value:`category:instrumentation`,prefix:`instrumentation/`,label:`Observability`,hint:`Trace, evaluate, and monitor your agent`,browseLabel:`Browse observability integrations`}];function itemLabel(e){return e.title===void 0?(e.name.split(`/`).at(-1)??e.name).split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `):e.title}function itemRows(e){return e.map((e,t)=>({value:`item:${t}`,label:itemLabel(e),hint:e.description??e.type}))}function categoryRows(){return[...REGISTRY_CATEGORIES.map(e=>({value:e.value,label:e.label,hint:e.hint})),{value:ALL,label:`Browse all`,hint:`Search every integration or enter an item address`},{value:DONE,label:`Return to chat`,trailingAction:!0}]}function categoryFor(e){return REGISTRY_CATEGORIES.find(t=>t.value===e)}function itemsForCategory(e,t){if(t===ALL)return e;let n=categoryFor(t);return n===void 0?e:e.filter(e=>e.address.startsWith(n.prefix)||e.name.startsWith(n.prefix))}function stringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function itemSource(e){if(e.startsWith(`@`))return e.split(`/`)[0]??e;if(/^https?:\/\//u.test(e))try{return new URL(e).host}catch{return e}return`Vercel`}function manifestRecord(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:{}}function summarizeDetails(e,t=3){let n=e.slice(0,t),r=e.length-n.length;return r>0?`${n.join(`, `)} … (+${r} more)`:n.join(`, `)}function itemDetails(e,t){let n=[{label:`Source`,value:e.source}],r=[...stringArray(t.dependencies),...stringArray(t.devDependencies),...stringArray(t.registryDependencies)];r.length>0&&n.push({label:`Packages`,value:summarizeDetails(r)});let i=t.envVars;if(typeof i==`object`&&i&&!Array.isArray(i)){let e=Object.keys(i);e.length>0&&n.push({label:`Environment`,value:summarizeDetails(e)})}let a=(Array.isArray(t.files)?t.files:[]).flatMap(e=>{if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.target;return typeof t==`string`?[t]:[]});return a.length>0&&n.push({label:`Files`,value:summarizeDetails(a)}),n}async function inspectItem(e,n,r,i,a,o){let s=a??manifestRecord(await withSpinner(e,`Loading registry item…`,()=>n.getRegistryItemManifest(r,i.address)));for(;;){let t={message:typeof s.title==`string`?s.title:i.name,metadata:itemDetails(i,s),options:[{value:`add`,label:`Add to project`},{value:`back`,label:`Back`}]},a=typeof s.description==`string`?s.description:i.description;if(a!==void 0&&(t.description=a),await e.select(t)===`back`)return{kind:`back`};let c=e.log.spinner?.(`Installing ${itemLabel(i)} and dependencies…`);try{c?.stop();let install=()=>n.installRegistryItem(r,i.address,{silent:!0,prompter:e,signal:o});return{kind:`added`,output:await(e.withExclusiveTerminal?.(install)??install())}}finally{c?.stop()}}}async function resolveAddressItem(e,n,r,i){let a=manifestRecord(await withSpinner(e,`Loading registry item…`,()=>n.getRegistryItemManifest(r,i))),o={address:i,name:typeof a.name==`string`?a.name:i,source:itemSource(i)};return typeof a.type==`string`&&(o.type=a.type),typeof a.description==`string`&&(o.description=a.description),{item:o,manifest:a}}async function runRegistryFlow(a){let o;(a.deps?.browseRegistryCatalog===void 0||a.deps.getRegistryItemManifest===void 0||a.deps.installRegistryItem===void 0)&&(o=await import(`#cli/commands/registry.js`));let s={browseRegistryCatalog:a.deps?.browseRegistryCatalog??o.browseRegistryCatalog,getRegistryItemManifest:a.deps?.getRegistryItemManifest??o.getRegistryItemManifest,installRegistryItem:a.deps?.installRegistryItem??o.installRegistryItem},c=[];try{for(;;){a.signal?.throwIfAborted();let e=await withSpinner(a.prompter,`Loading registry…`,()=>s.browseRegistryCatalog(a.appRoot));c=[...c,...e.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))];let o=await a.prompter.select({message:`Add an integration`,options:categoryRows(),hintLayout:`inline`,notices:c});if(c=[],o===DONE)return{kind:`done`,addedItems:[]};let l=itemsForCategory(e.items,o),u=itemRows(l);u.push({value:BACK,label:`Back`,trailingAction:!0});let d=await a.prompter.select({message:categoryFor(o)?.browseLabel??`Browse integrations`,options:u,search:!0,placeholder:`Search integrations or enter an item address`,searchAction:{label:e=>`Add “${e}”`,value:e=>`${ADDRESS_PREFIX}${e.trim()}`},hintLayout:`inline`});if(d===BACK)continue;let f=d.startsWith(ADDRESS_PREFIX)?await resolveAddressItem(a.prompter,s,a.appRoot,d.slice(8)):{item:l[Number(d.slice(5))]};if(f.item===void 0)throw Error(`The selected registry item is no longer available.`);let p=await inspectItem(a.prompter,s,a.appRoot,f.item,`manifest`in f?f.manifest:void 0,a.signal);if(p.kind===`added`)return p.output.length===0?{kind:`done`,addedItems:[f.item.address]}:{kind:`done`,addedItems:[f.item.address],output:p.output}}}catch(t){if(t instanceof WizardCancelledError)return{kind:`cancelled`};throw t}}export{runRegistryFlow};
1
+ import{createRegistrySession}from"./registry-session.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{detectDeployment}from"#setup/project-resolution.js";import{withSpinner}from"#setup/with-spinner.js";const ADDRESS_PREFIX=`address:`,BACK=`action:back`,DONE=`action:done`,ALL=`category:all`,REGISTRY_CATEGORIES=[{value:`category:channel`,prefix:`channel/`,label:`Channels`,hint:`Where people talk to your agent — Web, Slack, Discord, Teams`,browseLabel:`Browse channels`},{value:`category:connection`,prefix:`connection/`,label:`MCP connections`,hint:`Connect services like Linear, Notion, GitHub, and Vercel`,browseLabel:`Browse MCP connections`},{value:`category:extension`,prefix:`extension/`,label:`Extensions`,hint:`Add browser automation, memory, and developer tools`,browseLabel:`Browse extensions`},{value:`category:instrumentation`,prefix:`instrumentation/`,label:`Observability`,hint:`Trace, evaluate, and monitor your agent`,browseLabel:`Browse observability integrations`}];var RegistryFlowFailedError=class extends Error{completed;constructor(e,t){super(e instanceof Error?e.message:String(e),{cause:e}),this.name=`RegistryFlowFailedError`,this.completed=t}};function itemLabel(e){return e.title===void 0?(e.name.split(`/`).at(-1)??e.name).split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `):e.title}function itemRows(e){return e.map((e,t)=>({value:`item:${t}`,label:itemLabel(e),hint:e.description??e.type}))}function categoryRows(){return[...REGISTRY_CATEGORIES.map(e=>({value:e.value,label:e.label,hint:e.hint})),{value:ALL,label:`Browse all`,hint:`Search every integration or enter an item address`},{value:DONE,label:`Return to chat`,trailingAction:!0}]}function categoryFor(e){return REGISTRY_CATEGORIES.find(t=>t.value===e)}function itemsForCategory(e,t){if(t===ALL)return e;let n=categoryFor(t);return n===void 0?e:e.filter(e=>e.address.startsWith(n.prefix)||e.name.startsWith(n.prefix))}function stringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function itemSource(e){if(e.startsWith(`@`))return e.split(`/`)[0]??e;if(/^https?:\/\//u.test(e))try{return new URL(e).host}catch{return e}return`Vercel`}function manifestRecord(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:{}}function summarizeDetails(e,t=3){let n=e.slice(0,t),r=e.length-n.length;return r>0?`${n.join(`, `)} … (+${r} more)`:n.join(`, `)}function itemDetails(e,t){let n=[{label:`Source`,value:e.source}],r=[...stringArray(t.dependencies),...stringArray(t.devDependencies),...stringArray(t.registryDependencies)];r.length>0&&n.push({label:`Packages`,value:summarizeDetails(r)});let i=t.envVars;if(typeof i==`object`&&i&&!Array.isArray(i)){let e=Object.keys(i);e.length>0&&n.push({label:`Environment`,value:summarizeDetails(e)})}let a=(Array.isArray(t.files)?t.files:[]).flatMap(e=>{if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.target;return typeof t==`string`?[t]:[]});return a.length>0&&n.push({label:`Files`,value:summarizeDetails(a)}),n}async function inspectItem(e,t,n,r,a,o){let s=a??manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r.address)));for(;;){let i={message:typeof s.title==`string`?s.title:r.name,metadata:itemDetails(r,s),options:[{value:`add`,label:`Add to project`},{value:`back`,label:`Back`}]},a=typeof s.description==`string`?s.description:r.description;if(a!==void 0&&(i.description=a),await e.select(i)===`back`)return{kind:`back`};let c=e.log.spinner?.(`Installing ${itemLabel(r)} and dependencies…`);try{c?.stop();let install=()=>t.installRegistryItem(n,r.address,{silent:!0,prompter:e,signal:o});return{kind:`added`,...await(e.withExclusiveTerminal?.(install)??install())}}finally{c?.stop()}}}async function resolveAddressItem(e,t,n,r){let a=manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r))),o={address:r,name:typeof a.name==`string`?a.name:r,source:itemSource(r)};return typeof a.type==`string`&&(o.type=a.type),typeof a.description==`string`&&(o.description=a.description),{item:o,manifest:a}}async function runRegistryFlow(c){let l;(c.deps?.browseRegistryCatalog===void 0||c.deps.getRegistryItemManifest===void 0||c.deps.installRegistryItem===void 0)&&(l=await import(`#cli/commands/registry.js`));let u={browseRegistryCatalog:c.deps?.browseRegistryCatalog??l.browseRegistryCatalog,detectDeployment:c.deps?.detectDeployment??detectDeployment,getRegistryItemManifest:c.deps?.getRegistryItemManifest??l.getRegistryItemManifest,installRegistryItem:c.deps?.installRegistryItem??l.installRegistryItem,runDeployFlow:c.deps?.runDeployFlow??runDeployFlow},d=[],f=createRegistrySession(u);try{for(;;){c.signal?.throwIfAborted();let e=await withSpinner(c.prompter,`Loading registry…`,()=>u.browseRegistryCatalog(c.appRoot));d=[...d,...e.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))];let t=await c.prompter.select({message:`Add an integration`,options:categoryRows(),hintLayout:`inline`,notices:d});if(d=[],t===DONE)return f.result();let n=itemsForCategory(e.items,t),r=itemRows(n);r.push({value:BACK,label:`Back`,trailingAction:!0});let l=await c.prompter.select({message:categoryFor(t)?.browseLabel??`Browse integrations`,options:r,search:!0,placeholder:`Search integrations or enter an item address`,searchAction:{label:e=>`Add “${e}”`,value:e=>`${ADDRESS_PREFIX}${e.trim()}`},hintLayout:`inline`});if(l===BACK)continue;let p=l.startsWith(ADDRESS_PREFIX)?await resolveAddressItem(c.prompter,u,c.appRoot,l.slice(8)):{item:n[Number(l.slice(5))]};if(p.item===void 0)throw Error(`The selected registry item is no longer available.`);let m=await inspectItem(c.prompter,u,c.appRoot,p.item,`manifest`in p?p.manifest:void 0,c.signal);if(m.kind!==`added`)continue;f.add(p.item.address,itemLabel(p.item),m.output,m.setup);let h=await f.continueAfterInstall({appRoot:c.appRoot,prompter:c.prompter,signal:c.signal});if(h!==`add-more`)return h}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};let t=f.result();throw t.addedItems.length>0?new RegistryFlowFailedError(e,t):e}}export{RegistryFlowFailedError,runRegistryFlow};
@@ -1 +1 @@
1
- import{runRemoteAuthFlow}from"../cli/dev/tui/remote-auth.js";import{InteractionRequired}from"./ask.js";import{runVercel}from"./primitives/run-vercel.js";import{detectDeployment,detectProjectResolution,projectProductionUrlFromResolution}from"./project-resolution.js";import{runVercelEnvPull}from"./run-vercel-link.js";import{createDefaultSetupState,requireProjectPath,snapshotSetupState}from"./state.js";import{getPackageManagerStrategy}from"./primitives/pm/index.js";import{linkProject,requireAuth,resolveProjectByNameOrId,resolveTeam}from"./vercel-project.js";import{createPromptCommandOutput}from"./cli/command-output.js";import"./cli/index.js";import{setupConnectionConnector}from"./connection-connector.js";import{WizardCancelledError}from"./step.js";import{runHeadless,runInteractive}from"./runner.js";import{HeadlessPromptError,createHeadlessPrompter,formatHeadlessEvent}from"./headless.js";import{createPrompter}from"./prompter.js";import{runPackageManagerInstall,runPnpmInstall,spawnPackageManager,spawnPnpm}from"./primitives/pm/run.js";import"./primitives/index.js";import{provisionSlackbot,reconcileSlackUid}from"./slackbot.js";export{HeadlessPromptError,InteractionRequired,WizardCancelledError,createDefaultSetupState,createHeadlessPrompter,createPromptCommandOutput,createPrompter,detectDeployment,detectProjectResolution,formatHeadlessEvent,getPackageManagerStrategy,linkProject,projectProductionUrlFromResolution,provisionSlackbot,reconcileSlackUid,requireAuth,requireProjectPath,resolveProjectByNameOrId,resolveTeam,runHeadless,runInteractive,runPackageManagerInstall,runPnpmInstall,runRemoteAuthFlow,runVercel,runVercelEnvPull,setupConnectionConnector,snapshotSetupState,spawnPackageManager,spawnPnpm};
1
+ import{runRemoteAuthFlow}from"../cli/dev/tui/remote-auth.js";import{InteractionRequired}from"./ask-signals.js";import"./ask.js";import{runVercel}from"./primitives/run-vercel.js";import{detectDeployment,detectProjectResolution,projectProductionUrlFromResolution}from"./project-resolution.js";import{runVercelEnvPull}from"./run-vercel-link.js";import{createDefaultSetupState,requireProjectPath,snapshotSetupState}from"./state.js";import{getPackageManagerStrategy}from"./primitives/pm/index.js";import{linkProject,requireAuth,resolveProjectByNameOrId,resolveTeam}from"./vercel-project.js";import{createPromptCommandOutput}from"./cli/command-output.js";import"./cli/index.js";import{setupConnectionConnector}from"./connection-connector.js";import{WizardCancelledError}from"./step.js";import{runHeadless,runInteractive}from"./runner.js";import{HeadlessPromptError,createHeadlessPrompter,formatHeadlessEvent}from"./headless.js";import{createPrompter}from"./prompter.js";import{runPackageManagerInstall,runPnpmInstall,spawnPackageManager,spawnPnpm}from"./primitives/pm/run.js";import"./primitives/index.js";import{provisionSlackbot,reconcileSlackUid}from"./slackbot.js";export{HeadlessPromptError,InteractionRequired,WizardCancelledError,createDefaultSetupState,createHeadlessPrompter,createPromptCommandOutput,createPrompter,detectDeployment,detectProjectResolution,formatHeadlessEvent,getPackageManagerStrategy,linkProject,projectProductionUrlFromResolution,provisionSlackbot,reconcileSlackUid,requireAuth,requireProjectPath,resolveProjectByNameOrId,resolveTeam,runHeadless,runInteractive,runPackageManagerInstall,runPnpmInstall,runRemoteAuthFlow,runVercel,runVercelEnvPull,setupConnectionConnector,snapshotSetupState,spawnPackageManager,spawnPnpm};
@@ -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";
4
+ import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
5
5
  import { configureDiscordInteractionsEndpoint, registerDiscordCommand, resolveDiscordApplication } from "./api.js";
6
6
  import { provisionDiscordConnector } from "./connect.js";
7
7
  export interface DiscordSetupDeps {
8
8
  configureEndpoint: typeof configureDiscordInteractionsEndpoint;
9
9
  deriveConnectorSlug: typeof deriveSlackConnectorSlug;
10
- ensureVercelProject: typeof ensureVercelProject;
11
10
  provisionConnector: typeof provisionDiscordConnector;
12
11
  registerCommand: typeof registerDiscordCommand;
13
12
  resolveApplication: typeof resolveDiscordApplication;
14
13
  writeTextFile: typeof writeTextFile;
15
14
  }
16
- /** Runs guided Discord connector, command, and channel setup. */
17
- export declare function setupDiscord(context: IntegrationSetupContext, deps?: DiscordSetupDeps): Promise<IntegrationSetupResult>;
18
- /** Discord setup registration. */
19
- export declare const DISCORD_SETUP: SetupIntegration;
15
+ export interface DiscordSetupPlan {
16
+ botToken: string;
17
+ commandName: string;
18
+ commandDescription: string;
19
+ project: VercelProjectReference;
20
+ slug: string;
21
+ }
22
+ export declare function prepareDiscordSetup(context: SetupPrepareContext, deps?: DiscordSetupDeps): Promise<DiscordSetupPlan>;
23
+ export declare function applyDiscordSetup(plan: DiscordSetupPlan, context: SetupApplyContext, deps?: DiscordSetupDeps): Promise<{
24
+ deploymentRequired: true;
25
+ facts: {
26
+ label: string;
27
+ value: string;
28
+ kind: "url";
29
+ }[];
30
+ }>;
31
+ export declare const DISCORD_SETUP: import("../types.js").SetupIntegration;
@@ -1,8 +1,9 @@
1
- import{configureDiscordInteractionsEndpoint,registerDiscordCommand,resolveDiscordApplication}from"./api.js";import{provisionDiscordConnector}from"./connect.js";import{join}from"node:path";import{deriveSlackConnectorSlug}from"#setup/scaffold/index.js";import{WizardCancelledError}from"#setup/step.js";import{text}from"#setup/ask.js";import{ensureVercelProject}from"#setup/flows/ensure-vercel-project.js";import{writeTextFile}from"#setup/scaffold/files.js";const defaultDeps={configureEndpoint:configureDiscordInteractionsEndpoint,deriveConnectorSlug:deriveSlackConnectorSlug,ensureVercelProject,provisionConnector:provisionDiscordConnector,registerCommand:registerDiscordCommand,resolveApplication:resolveDiscordApplication,writeTextFile};function validateCommandName(e){let t=e.trim();return t?t.length>32?`Command name must be 32 characters or fewer.`:/^[a-z0-9_-]+$/.test(t)?null:`Use lowercase letters, numbers, hyphens, or underscores.`:`Command name is required.`}function connectTemplate(e){return`import { connectDiscordCredentials } from "@vercel/connect/eve";
1
+ import{configureDiscordInteractionsEndpoint,registerDiscordCommand,resolveDiscordApplication}from"./api.js";import{provisionDiscordConnector}from"./connect.js";import{defineSetupIntegration}from"../types.js";import{join}from"node:path";import{deriveSlackConnectorSlug}from"#setup/scaffold/index.js";import{text}from"#setup/ask.js";import{writeTextFile}from"#setup/scaffold/files.js";const defaultDeps={configureEndpoint:configureDiscordInteractionsEndpoint,deriveConnectorSlug:deriveSlackConnectorSlug,provisionConnector:provisionDiscordConnector,registerCommand:registerDiscordCommand,resolveApplication:resolveDiscordApplication,writeTextFile};function validateCommandName(e){let t=e.trim();return t?t.length>32?`Command name must be 32 characters or fewer.`:/^[a-z0-9_-]+$/.test(t)?null:`Use lowercase letters, numbers, hyphens, or underscores.`:`Command name is required.`}function connectTemplate(e){return`import { connectDiscordCredentials } from "@vercel/connect/eve";
2
2
  import { discordChannel } from "eve/channels/discord";
3
3
 
4
4
  export default discordChannel({
5
5
  credentials: connectDiscordCredentials(${JSON.stringify(e)}),
6
6
  });
7
- `}async function setupDiscord(e,t=defaultDeps){if(e.environment.vercel.kind===`unavailable`)throw Error("Discord setup requires an authenticated Vercel CLI. Run `vercel login`, then retry.");try{let n=[`Create a Discord application or open an existing one, then go to Bot → Reset Token and copy the new bot token.`,`Create: https://discord.com/developers/applications?new_application=true`,`Existing applications: https://discord.com/developers/applications`];e.ui.prompter.acknowledge?await e.ui.prompter.acknowledge({message:`Discord application`,lines:n}):e.ui.prompter.log.info(`Discord application\n${n.join(`
8
- `)}`);let r=(await e.ui.asker.ask(text({key:`discord-bot-token`,message:`Discord bot token`,required:!0,sensitive:!0}))).trim(),i=await e.ui.asker.ask(text({key:`discord-command-name`,message:`Discord command name`,detected:`ask`,required:!0,validate:validateCommandName})),a=await e.ui.asker.ask(text({key:`discord-command-description`,message:`Discord command description`,detected:`Ask the eve agent`,required:!0,validate:e=>e.trim().length===0?`Command description is required.`:e.trim().length>100?`Command description must be 100 characters or fewer.`:null})),o=await t.resolveApplication(r),s=await t.ensureVercelProject({appRoot:e.appRoot,prompter:e.ui.prompter,signal:e.signal}),c=await t.provisionConnector({botToken:r,log:e.ui.prompter.log,project:s,projectRoot:e.appRoot,slug:await t.deriveConnectorSlug(e.appRoot),signal:e.signal});await t.registerCommand(o.id,r,{name:i.trim(),description:a.trim()}),await t.configureEndpoint(r,c.id),await t.writeTextFile(join(e.appRoot,`agent/channels/discord.ts`),connectTemplate(c.uid),{force:e.force});let l=`https://discord.com/oauth2/authorize?client_id=${encodeURIComponent(o.id)}&scope=bot%20applications.commands&permissions=3072`;return e.ui.nextSteps([`Install the Discord application, then try /${i.trim()}: ${l}`]),{kind:`done`,facts:[{label:`Discord application`,value:`https://discord.com/developers/applications/${o.id}/information`,kind:`url`}]}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}const DISCORD_SETUP={kind:`discord`,label:`Discord`,hint:`Slash commands and interactions`,setup:setupDiscord};export{DISCORD_SETUP,setupDiscord};
7
+ `}async function prepareDiscordSetup(e,t=defaultDeps){if(e.environment.vercel.kind===`unavailable`)throw Error("Discord setup requires an authenticated Vercel CLI. Run `vercel login`, then retry.");e.presenter.log.info(`Create a Discord application or open an existing one, then go to Bot → Reset Token and copy the new bot token.
8
+ Create: https://discord.com/developers/applications?new_application=true
9
+ Existing applications: https://discord.com/developers/applications`);let n=(await e.asker.ask(text({key:`discord-bot-token`,message:`Discord bot token`,required:!0,sensitive:!0,environment:`DISCORD_BOT_TOKEN`}))).trim(),r=await e.asker.ask(text({key:`discord-command-name`,message:`Discord command name`,detected:`ask`,required:!0,validate:validateCommandName})),i=await e.asker.ask(text({key:`discord-command-description`,message:`Discord command description`,detected:`Ask the eve agent`,required:!0,validate:e=>e.trim().length===0?`Command description is required.`:e.trim().length>100?`Command description must be 100 characters or fewer.`:null})),a=await e.resolveVercelProject(`Discord`);return{botToken:n,commandName:r.trim(),commandDescription:i.trim(),project:a,slug:await t.deriveConnectorSlug(e.appRoot)}}async function applyDiscordSetup(e,t,n=defaultDeps){let r=await n.resolveApplication(e.botToken),i=await n.provisionConnector({botToken:e.botToken,log:t.presenter.log,project:e.project,projectRoot:t.appRoot,slug:e.slug,signal:t.signal});await n.registerCommand(r.id,e.botToken,{name:e.commandName,description:e.commandDescription}),await n.configureEndpoint(e.botToken,i.id),await n.writeTextFile(join(t.appRoot,`agent/channels/discord.ts`),connectTemplate(i.uid),{force:t.force});let o=`https://discord.com/oauth2/authorize?client_id=${encodeURIComponent(r.id)}&scope=bot%20applications.commands&permissions=3072`;return t.presenter.nextSteps([`Install the Discord application, then try /${e.commandName}: ${o}`]),{deploymentRequired:!0,facts:[{label:`Discord application dashboard`,value:`https://discord.com/developers/applications/${r.id}/information`,kind:`url`}]}}const DISCORD_SETUP=defineSetupIntegration({kind:`discord`,label:`Discord`,hint:`Slash commands and interactions`,prepare:prepareDiscordSetup,apply:applyDiscordSetup});export{DISCORD_SETUP,applyDiscordSetup,prepareDiscordSetup};
@@ -1,15 +1,44 @@
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";
4
+ import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
5
5
  import { provisionGitHubConnector } from "./connect.js";
6
6
  export interface GitHubSetupDeps {
7
7
  deriveConnectorSlug: typeof deriveSlackConnectorSlug;
8
- ensureVercelProject: typeof ensureVercelProject;
9
8
  provisionConnector: typeof provisionGitHubConnector;
10
9
  writeTextFile: typeof writeTextFile;
11
10
  }
12
- /** Runs guided GitHub App connector and channel setup. */
13
- export declare function setupGitHub(context: IntegrationSetupContext, deps?: GitHubSetupDeps): Promise<IntegrationSetupResult>;
14
- /** GitHub App setup registration. */
15
- export declare const GITHUB_SETUP: SetupIntegration;
11
+ declare const GITHUB_EVENT_OPTIONS: readonly [{
12
+ readonly id: "issue_comment";
13
+ readonly label: "New issue and PR comments";
14
+ readonly value: "issue_comment";
15
+ readonly hint: "Reply when a new timeline comment includes `@<bot-name>`.";
16
+ }, {
17
+ readonly id: "pull_request_review_comment";
18
+ readonly label: "New inline PR review comments";
19
+ readonly value: "pull_request_review_comment";
20
+ readonly hint: "Reply when a new inline review comment includes `@<bot-name>`.";
21
+ }, {
22
+ readonly id: "issues";
23
+ readonly label: "New issues";
24
+ readonly value: "issues";
25
+ readonly hint: "Add comments to new issues.";
26
+ }, {
27
+ readonly id: "pull_request";
28
+ readonly label: "New PRs";
29
+ readonly value: "pull_request";
30
+ readonly hint: "Add comments to new pull requests.";
31
+ }];
32
+ type GitHubWebhookEvent = (typeof GITHUB_EVENT_OPTIONS)[number]["value"];
33
+ export interface GitHubSetupPlan {
34
+ events: readonly GitHubWebhookEvent[];
35
+ project: VercelProjectReference;
36
+ slug: string;
37
+ }
38
+ export declare function prepareGitHubSetup(context: SetupPrepareContext, deps?: GitHubSetupDeps): Promise<GitHubSetupPlan>;
39
+ export declare function applyGitHubSetup(plan: GitHubSetupPlan, context: SetupApplyContext, deps?: GitHubSetupDeps): Promise<{
40
+ facts: never[];
41
+ deploymentRequired: true;
42
+ }>;
43
+ export declare const GITHUB_SETUP: import("../types.js").SetupIntegration;
44
+ export {};
@@ -1,4 +1,4 @@
1
- import{provisionGitHubConnector}from"./connect.js";import{join}from"node:path";import{deriveSlackConnectorSlug}from"#setup/scaffold/index.js";import{WizardCancelledError}from"#setup/step.js";import{ensureVercelProject}from"#setup/flows/ensure-vercel-project.js";import{writeTextFile}from"#setup/scaffold/files.js";const defaultDeps={deriveConnectorSlug:deriveSlackConnectorSlug,ensureVercelProject,provisionConnector:provisionGitHubConnector,writeTextFile},githubEventsQuestion={key:`github-events`,message:`What should this GitHub App respond to?`,options:[{id:`issue_comment`,label:`New issue and PR comments`,value:`issue_comment`,hint:"Reply when a new timeline comment includes `@<bot-name>`."},{id:`pull_request_review_comment`,label:`New inline PR review comments`,value:`pull_request_review_comment`,hint:"Reply when a new inline review comment includes `@<bot-name>`."},{id:`issues`,label:`New issues`,value:`issues`,hint:`Add comments to new issues.`},{id:`pull_request`,label:`New PRs`,value:`pull_request`,hint:`Add comments to new pull requests.`}],recommended:[`issue_comment`,`pull_request_review_comment`],requireSelection:!0};function connectTemplate(e,t,n){let r=[n.includes(`issues`)?` onIssue(ctx, issue) {
1
+ import{defineSetupIntegration}from"../types.js";import{provisionGitHubConnector}from"./connect.js";import{join}from"node:path";import{deriveSlackConnectorSlug}from"#setup/scaffold/index.js";import{writeTextFile}from"#setup/scaffold/files.js";const defaultDeps={deriveConnectorSlug:deriveSlackConnectorSlug,provisionConnector:provisionGitHubConnector,writeTextFile},githubEventsQuestion={key:`github-events`,message:`What should this GitHub App respond to?`,options:[{id:`issue_comment`,label:`New issue and PR comments`,value:`issue_comment`,hint:"Reply when a new timeline comment includes `@<bot-name>`."},{id:`pull_request_review_comment`,label:`New inline PR review comments`,value:`pull_request_review_comment`,hint:"Reply when a new inline review comment includes `@<bot-name>`."},{id:`issues`,label:`New issues`,value:`issues`,hint:`Add comments to new issues.`},{id:`pull_request`,label:`New PRs`,value:`pull_request`,hint:`Add comments to new pull requests.`}],recommended:[`issue_comment`,`pull_request_review_comment`],required:!0,requireSelection:!0};function connectTemplate(e,t,n){let r=[n.includes(`issues`)?` onIssue(ctx, issue) {
2
2
  if (issue.action !== "opened") return null;
3
3
  return { auth: defaultGitHubAuth(ctx) };
4
4
  },`:void 0,n.includes(`pull_request`)?` onPullRequest(ctx, pullRequest) {
@@ -12,4 +12,4 @@ export default githubChannel({
12
12
  botName: ${JSON.stringify(t)},
13
13
  credentials: connectGitHubCredentials(${JSON.stringify(e)}),${a}
14
14
  });
15
- `}async function setupGitHub(e,n=defaultDeps){if(e.environment.vercel.kind===`unavailable`)throw Error("GitHub setup requires an authenticated Vercel CLI. Run `vercel login`, then retry.");try{e.ui.prompter.log.info(`GitHub App`),e.ui.prompter.log.info(`Vercel Connect creates a GitHub App and routes verified webhooks to your deployed agent.`);let r=await e.ui.asker.askMany(githubEventsQuestion),i=await n.ensureVercelProject({appRoot:e.appRoot,prompter:e.ui.prompter,signal:e.signal}),a=await n.provisionConnector({log:e.ui.prompter.log,events:r,project:i,projectRoot:e.appRoot,slug:await n.deriveConnectorSlug(e.appRoot),signal:e.signal});return await n.writeTextFile(join(e.appRoot,`agent/channels/github.ts`),connectTemplate(a.uid,a.appSlug,r),{force:e.force}),e.ui.nextSteps([`Deploy the agent, then open the GitHub App in Vercel Connect and install it in the organization or account where you want to use it.`,`Add @${a.appSlug} to a new issue, pull request, or review comment to invoke the agent. GitHub may not autocomplete or render the token as a linked mention.`]),{kind:`done`,facts:[{label:`Vercel Connect`,value:`https://vercel.com/d?to=/%5Bteam%5D/~/connect&title=Open+Vercel+Connect`,kind:`url`}]}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}const GITHUB_SETUP={kind:`github`,label:`GitHub`,hint:`Respond to issues, pull requests, and comments`,setup:setupGitHub};export{GITHUB_SETUP,setupGitHub};
15
+ `}async function prepareGitHubSetup(e,t=defaultDeps){if(e.environment.vercel.kind===`unavailable`)throw Error("GitHub setup requires an authenticated Vercel CLI. Run `vercel login`, then retry.");return{events:await e.asker.askMany(githubEventsQuestion),project:await e.resolveVercelProject(`GitHub`),slug:await t.deriveConnectorSlug(e.appRoot)}}async function applyGitHubSetup(e,t,r=defaultDeps){t.presenter.log.info(`GitHub App`),t.presenter.log.info(`Vercel Connect creates a GitHub App and routes verified webhooks to your deployed agent.`);let i=await r.provisionConnector({log:t.presenter.log,events:e.events,project:e.project,projectRoot:t.appRoot,slug:e.slug,signal:t.signal});return await r.writeTextFile(join(t.appRoot,`agent/channels/github.ts`),connectTemplate(i.uid,i.appSlug,e.events),{force:t.force}),t.presenter.nextSteps([`Deploy the agent, then open the GitHub App in Vercel Connect and install it in the organization or account where you want to use it.`,`Add @${i.appSlug} to a new issue, pull request, or review comment to invoke the agent. GitHub may not autocomplete or render the token as a linked mention.`]),{facts:[],deploymentRequired:!0}}const GITHUB_SETUP=defineSetupIntegration({kind:`github`,label:`GitHub`,hint:`Respond to issues, pull requests, and comments`,prepare:prepareGitHubSetup,apply:applyGitHubSetup});export{GITHUB_SETUP,applyGitHubSetup,prepareGitHubSetup};
@@ -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};