eve 0.66.2 → 0.66.3

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 (141) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/src/cli/commands/registry-declared-setups.d.ts +0 -1
  3. package/dist/src/cli/commands/registry-declared-setups.js +1 -1
  4. package/dist/src/cli/commands/registry.d.ts +5 -1
  5. package/dist/src/cli/commands/registry.js +1 -1
  6. package/dist/src/cli/dev/tui/argument-typeahead.d.ts +1 -2
  7. package/dist/src/cli/dev/tui/argument-typeahead.js +2 -2
  8. package/dist/src/cli/dev/tui/blocks.d.ts +5 -1
  9. package/dist/src/cli/dev/tui/blocks.js +3 -3
  10. package/dist/src/cli/dev/tui/flow-drawer.d.ts +11 -0
  11. package/dist/src/cli/dev/tui/flow-drawer.js +1 -0
  12. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  13. package/dist/src/cli/dev/tui/prompt-commands.d.ts +199 -0
  14. package/dist/src/cli/dev/tui/prompt-commands.js +2 -2
  15. package/dist/src/cli/dev/tui/registry-result-message.d.ts +15 -4
  16. package/dist/src/cli/dev/tui/registry-result-message.js +4 -4
  17. package/dist/src/cli/dev/tui/runner.d.ts +18 -6
  18. package/dist/src/cli/dev/tui/runner.js +3 -2
  19. package/dist/src/cli/dev/tui/setup-commands.d.ts +5 -22
  20. package/dist/src/cli/dev/tui/setup-commands.js +2 -1
  21. package/dist/src/cli/dev/tui/setup-flow.d.ts +2 -10
  22. package/dist/src/cli/dev/tui/setup-panel.d.ts +4 -23
  23. package/dist/src/cli/dev/tui/setup-panel.js +3 -3
  24. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +11 -7
  25. package/dist/src/cli/dev/tui/terminal-renderer.js +11 -10
  26. package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
  27. package/dist/src/cli/dev/tui/tui.js +1 -1
  28. package/dist/src/compiled/.vendor-stamp.json +6 -6
  29. package/dist/src/compiled/@workflow/core/create-hook.d.ts +38 -1
  30. package/dist/src/compiled/@workflow/core/global.d.ts +2 -0
  31. package/dist/src/compiled/@workflow/core/index.js +2 -2
  32. package/dist/src/compiled/@workflow/core/private.js +1 -1
  33. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +9 -0
  34. package/dist/src/compiled/@workflow/core/runtime/helpers.js +1 -1
  35. package/dist/src/compiled/@workflow/core/runtime/hook-wake.d.ts +89 -0
  36. package/dist/src/compiled/@workflow/core/runtime/open-hook-wait-state.d.ts +55 -0
  37. package/dist/src/compiled/@workflow/core/runtime/quickjs-runtime.d.ts +2 -0
  38. package/dist/src/compiled/@workflow/core/runtime/suspension-handler.d.ts +8 -1
  39. package/dist/src/compiled/@workflow/core/runtime.js +4 -4
  40. package/dist/src/compiled/@workflow/core/serialization/types.d.ts +8 -0
  41. package/dist/src/compiled/@workflow/core/serialization.d.ts +8 -0
  42. package/dist/src/compiled/@workflow/core/serialization.js +1 -1
  43. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  44. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  45. package/dist/src/compiled/@workflow/errors/index.d.ts +23 -0
  46. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  47. package/dist/src/compiled/@workflow/world/events.d.ts +34 -0
  48. package/dist/src/compiled/@workflow/world/hooks.d.ts +27 -0
  49. package/dist/src/compiled/@workflow/world/index.d.ts +2 -2
  50. package/dist/src/compiled/@workflow/world/interfaces.d.ts +24 -0
  51. package/dist/src/compiled/@workflow/world/spec-version.d.ts +32 -11
  52. package/dist/src/compiled/@workflow/world-local/index.js +3 -3
  53. package/dist/src/compiled/@workflow/world-vercel/index.js +17 -17
  54. package/dist/src/compiled/_chunks/workflow/{attribute-changes-89Nwz7XW.js → attribute-changes-DzJovO_f.js} +1 -1
  55. package/dist/src/compiled/_chunks/workflow/dist-BQSf8Hax.js +1 -0
  56. package/dist/src/compiled/_chunks/workflow/dist-BfMqXLK3.js +3 -0
  57. package/dist/src/compiled/_chunks/workflow/frames-qKc30rjg.js +3 -0
  58. package/dist/src/compiled/_chunks/workflow/{global-DIlFoH2w.js → global-BQnKzRXo.js} +1 -1
  59. package/dist/src/compiled/_chunks/workflow/{helpers-k3zcudWr.js → helpers-CNMO7zB1.js} +1 -1
  60. package/dist/src/compiled/_chunks/workflow/{logger-Bzc8jWWj.js → logger-BCEj9caG.js} +1 -1
  61. package/dist/src/compiled/_chunks/workflow/resume-hook-QS_cMwHn.js +1 -0
  62. package/dist/src/compiled/_chunks/workflow/run-CA5wWV2S.js +1 -0
  63. package/dist/src/compiled/_chunks/workflow/serialization-CJ265vSe.js +3 -0
  64. package/dist/src/compiled/_chunks/workflow/sleep-CBn94Ayv.js +1 -0
  65. package/dist/src/compiled/_chunks/workflow/{wait-until-DXLZx3tU.js → wait-until-Bo2U6ON9.js} +1 -1
  66. package/dist/src/compiled/_chunks/workflow/{ws-transport-BSM_Cl7a.js → ws-transport-BrsMYqJg.js} +1 -1
  67. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  68. package/dist/src/execution/session/entry-input.d.ts +4 -0
  69. package/dist/src/execution/session/entry-input.js +1 -1
  70. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  71. package/dist/src/execution/tools/ask-question-workflow.js +1 -1
  72. package/dist/src/execution/tools/sleep-workflow.js +1 -1
  73. package/dist/src/execution/workflow-runtime.js +1 -1
  74. package/dist/src/extensions/code/extension/lib/connect-authentication.js +1 -1
  75. package/dist/src/extensions/code/extension/lib/github-shell.js +1 -1
  76. package/dist/src/extensions/code/extension/lib/prwatch-github.js +1 -1
  77. package/dist/src/harness/tool-loop.js +1 -1
  78. package/dist/src/internal/application/package.js +1 -1
  79. package/dist/src/internal/workflow/development-world-client.js +1 -1
  80. package/dist/src/public/channels/slack/activity-plan.d.ts +2 -1
  81. package/dist/src/public/channels/slack/activity-plan.js +1 -1
  82. package/dist/src/public/channels/slack/activity.d.ts +2 -0
  83. package/dist/src/public/channels/slack/activity.js +1 -1
  84. package/dist/src/public/channels/slack/api.d.ts +7 -0
  85. package/dist/src/public/channels/slack/api.js +1 -1
  86. package/dist/src/public/channels/slack/attachments.d.ts +5 -2
  87. package/dist/src/public/channels/slack/attachments.js +1 -1
  88. package/dist/src/public/channels/slack/index.d.ts +1 -0
  89. package/dist/src/public/channels/slack/input-response.d.ts +2 -0
  90. package/dist/src/public/channels/slack/input-response.js +1 -1
  91. package/dist/src/public/channels/slack/interaction-cards.js +1 -1
  92. package/dist/src/public/channels/slack/interactions.d.ts +2 -0
  93. package/dist/src/public/channels/slack/interactions.js +1 -1
  94. package/dist/src/public/channels/slack/slackChannel.d.ts +3 -0
  95. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  96. package/dist/src/public/channels/slack/slash-command.d.ts +5 -1
  97. package/dist/src/public/channels/slack/slash-command.js +1 -1
  98. package/dist/src/public/channels/slack/transport.d.ts +76 -0
  99. package/dist/src/public/channels/slack/transport.js +1 -0
  100. package/dist/src/setup/cli/channel-setup-prompter.d.ts +2 -9
  101. package/dist/src/setup/cli/channel-setup-prompter.js +1 -1
  102. package/dist/src/setup/cli/index.d.ts +1 -1
  103. package/dist/src/setup/connection-connector.js +1 -1
  104. package/dist/src/setup/flows/model-login-connection.d.ts +0 -16
  105. package/dist/src/setup/flows/model-login-connection.js +1 -1
  106. package/dist/src/setup/flows/model-login-options.d.ts +59 -0
  107. package/dist/src/setup/flows/model-login-options.js +1 -0
  108. package/dist/src/setup/flows/model-login.js +1 -1
  109. package/dist/src/setup/flows/model-source-change.d.ts +0 -2
  110. package/dist/src/setup/flows/model-source-change.js +1 -1
  111. package/dist/src/setup/flows/registry-session.d.ts +15 -13
  112. package/dist/src/setup/flows/registry-session.js +1 -1
  113. package/dist/src/setup/flows/registry.js +1 -1
  114. package/dist/src/setup/flows/vercel-model-login.js +1 -1
  115. package/dist/src/setup/integrations/photon/setup-flow.js +1 -1
  116. package/dist/src/setup/prompter.d.ts +2 -2
  117. package/dist/src/setup/registry-setup-client.js +1 -1
  118. package/dist/src/setup/registry-setup-protocol.d.ts +0 -4
  119. package/dist/src/setup/scaffold/create/project.js +1 -1
  120. package/dist/src/setup/slack-connect-lifecycle.js +1 -1
  121. package/docs/channels/slack.mdx +31 -2
  122. package/docs/guides/dev-tui.md +1 -1
  123. package/package.json +6 -6
  124. package/dist/src/compiled/_chunks/workflow/dist-CNT2vjZ4.js +0 -1
  125. package/dist/src/compiled/_chunks/workflow/dist-DtXwQbZr.js +0 -3
  126. package/dist/src/compiled/_chunks/workflow/frames-DnrfSd1P.js +0 -3
  127. package/dist/src/compiled/_chunks/workflow/resume-hook-BmJ263ZR.js +0 -1
  128. package/dist/src/compiled/_chunks/workflow/run-NLvREued.js +0 -1
  129. package/dist/src/compiled/_chunks/workflow/serialization-HCDeQWcR.js +0 -3
  130. package/dist/src/compiled/_chunks/workflow/sleep-Bi508qmg.js +0 -1
  131. package/dist/src/public/channels/slack/api-errors.d.ts +0 -33
  132. package/dist/src/public/channels/slack/api-errors.js +0 -1
  133. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/authorization.js +0 -0
  134. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/connector.js +0 -0
  135. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/index.js +0 -0
  136. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/installation.js +0 -0
  137. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/internal/base-url.js +0 -0
  138. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/internal/default-scopes.js +0 -0
  139. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/internal/env.js +0 -0
  140. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/internal/url-validation.js +0 -0
  141. /package/dist/src/node_modules/.pnpm/{@vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_37d5e4f23f0f03cba2e0819e67e81b38 → @vercel_connect@2.2.0_@ai-sdk_mcp@2.0.52_zod@4.5.4__@auth_core@0.41.2_@chat-adapter_sla_3fe44d656b2dcd93a04011e80966afff}/node_modules/@vercel/connect/dist/token.js +0 -0
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#shared/input.js";import{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{isSlackResponseError}from"#public/channels/slack/api-errors.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildFreeformModalView,decodeFreeformHitlActionId,deriveHitlResponse,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{updateAnsweredFreeformCard,updateAnsweredHitlCard}from"#public/channels/slack/interaction-cards.js";import{approvalResponderStatePatch,authorizeInputResponse}from"#public/channels/slack/input-response.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{dispatchSlashCommand}from"#public/channels/slack/slash-command.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=s.team_id??o?.id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,installationTeamId:readInstallationTeamId(t),threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,installationTeamId:readInstallationTeamId(e.raw),threadTs:e.threadTs,teamId:e.user?.teamId??e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlocks(e)[0],n=readSlackTextObject(t?.text);return n.length>0?n:void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}function readInstallationTeamId(e){if(!isObjectRecord(e))return;let t=isObjectRecord(e.view)?e.view:void 0,n=isObjectRecord(e.team)?e.team:void 0,r=isObjectRecord(e.user)?e.user:void 0;return[t?.app_installed_team_id,e.app_installed_team_id,n?.id,r?.team_id,t?.team_id].find(e=>typeof e==`string`&&e.length>0)}async function handleInteractionPost(e,t,n){let r=new Response(`ok`,{status:200}),i;try{i=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),r}if(i.kind===`view_submission`)return handleViewSubmission(i,t,n);if(i.kind===`slash_command`)return dispatchSlashCommand(i,t,n.config),new Response(null,{status:200});if(i.kind===`unsupported`){let e=parseShortcutPayload(i.raw);return e===null?(log.warn(`unsupported Slack interaction payload ignored`,{type:i.type}),r):(dispatchShortcut(e,readInstallationTeamId(i.raw),t,n),new Response(null,{status:200}))}if(i.kind!==`block_actions`)return log.warn(`unsupported Slack interaction payload ignored`,{type:i.kind}),r;let a=parseBlockActionsPayload(i);if(!a)return r;let o=a.actions.find(e=>isFreeformAction(e.actionId));if(o)return await openFreeformModal({payload:i.raw,interaction:a,freeformAction:o,deps:n}),r;let s=a.actions.flatMap(e=>{let t=deriveHitlResponse(e);return t===null?[]:[{action:e,derived:t}]});if(s.length>0){let e=s[0].action.user,r=s[0].derived.route;t.waitUntil(dispatchBlockInputResponses({ctx:t,deps:n,interaction:a,route:r,submission:{type:`block_actions`,actions:s.map(({action:e})=>e),inputResponses:s.map(({derived:e})=>e.response),messageTs:s[0].action.messageTs,user:e}}))}let c=n.config.onInteraction;if(c){let e=a.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let r=e[0].user,{thread:i,slack:o}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:a.channelId,threadTs:a.threadTs,installationTeamId:a.installationTeamId,teamId:a.teamId}),s={...bindSlackSessionOperations({address:slackContinuationToken(a.channelId,a.threadTs),defaultAuth:buildSlackAuthContext({channelId:a.channelId,teamId:a.teamId,threadTs:a.threadTs,userId:r.id,userName:r.username??r.name}),from:t.from,resolveSession:t.resolveSession,state:{channelId:a.channelId,installationTeamId:a.installationTeamId??null,teamId:a.teamId??null,threadTs:a.threadTs,triggeringUserId:r.id}}),thread:i,slack:o};for(let n of e)t.waitUntil(Promise.resolve(c(n,s)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return r}function parseShortcutPayload(e){if(!isObjectRecord(e))return null;let t=e.type;if(t!==`message_action`&&t!==`shortcut`)return null;let n=readRequiredString(e.callback_id),r=readRequiredString(e.trigger_id),i=isObjectRecord(e.user)?e.user:void 0,a=readRequiredString(i?.id);if(n===null||r===null||a===null)return null;let o=isObjectRecord(e.team)?e.team:void 0,s=readOptionalString(i?.team_id)??readOptionalString(o?.id),c={id:a,username:readOptionalString(i?.username),name:readOptionalString(i?.name)};if(t===`shortcut`)return{type:t,callbackId:n,triggerId:r,user:c,teamId:s};let l=isObjectRecord(e.channel)?e.channel:void 0,u=isObjectRecord(e.message)?e.message:void 0,d=readRequiredString(l?.id),f=readRequiredString(u?.ts);return d===null||f===null||u===void 0?null:{type:t,callbackId:n,triggerId:r,user:c,teamId:s,channelId:d,message:{text:typeof u.text==`string`?u.text:``,ts:f,threadTs:readOptionalString(u.thread_ts),userId:readOptionalString(u.user)},responseUrl:readOptionalString(e.response_url)}}function readRequiredString(e){return typeof e==`string`&&e.length>0?e:null}function readOptionalString(e){return typeof e==`string`&&e.length>0?e:void 0}function dispatchShortcut(e,t,n,r){let i=r.config.onShortcut;if(i===void 0){log.warn(`Slack shortcut ignored because onShortcut is not configured`,{type:e.type});return}let a=buildShortcutContext({config:r.config,installationTeamId:t,teamId:e.teamId});dispatchInteractionHook(()=>i(e,a),n,`shortcut handler failed`)}function buildShortcutContext(e){return{slack:buildSlackWorkspaceHandle({botToken:e.config.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:e.teamId})}}function dispatchInteractionHook(e,t,n){t.waitUntil(Promise.resolve().then(e).catch(e=>{log.error(n,{error:e})}))}async function dispatchBlockInputResponses(e){let t=e.route?.channelId??e.interaction.channelId,n=e.route?.threadTs??e.interaction.threadTs,r=await authorizeInputResponse({channelId:t,deps:e.deps,installationTeamId:e.interaction.installationTeamId,submission:e.submission,teamId:e.interaction.teamId,threadTs:n});if(r!==null){try{await e.ctx.from(slackContinuationToken(t,n)).respond(e.submission.inputResponses,{auth:r.auth,state:approvalResponderStatePatch(e.submission,r.auth)})}catch(e){log.error(`HITL interaction delivery failed`,{error:e});return}if(!e.submission.actions.some(e=>deriveHitlResponse(e)?.kind===`tool-approval`))try{await updateAnsweredHitlCard(e.interaction,e.deps)}catch(e){log.error(`HITL answered-card update failed`,{error:e})}}}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=decodeFreeformHitlActionId(e.freeformAction.actionId);if(n===null){log.warn(`freeform button click carries invalid metadata`);return}let r=n.requestId;if(!r){log.warn(`freeform button click missing requestId`);return}let i=e.freeformAction.messageTs;if(!i){log.warn(`freeform button click missing messageTs`);return}let a=n?.route?.channelId??e.interaction.channelId,o=n?.route?.threadTs??e.interaction.threadTs,s={continuationToken:slackContinuationToken(a,o),channelId:a,installationTeamId:e.interaction.installationTeamId,messageChannelId:e.interaction.channelId,threadTs:o,messageTs:i,requestId:r},c=readPromptTextFromBlocks(e.interaction.messageBlocks),l=buildFreeformModalView({metadata:s,prompt:c}),u=await resolveSlackBotToken(e.deps.config.credentials?.botToken,{teamId:e.interaction.installationTeamId});try{await callSlackApi({botToken:u,operation:`views.open`,body:{trigger_id:t,view:l}})}catch(e){if(!isSlackResponseError(e))throw e;log.error(`Slack views.open failed`,{error:e})}}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let i;try{i=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!i.continuationToken||!i.requestId||!i.messageTs||!i.channelId||!i.threadTs)return r;let a=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(a.length===0)return r;let o=e.user,s=e.userId,c=o?.teamId??e.teamId??null,l=i.installationTeamId??readInstallationTeamId(e.raw),u={type:`view_submission`,inputResponses:[parseInputResponse({requestId:i.requestId,text:a})],messageTs:i.messageTs,user:{id:s,username:o?.username,name:o?.name}};return t.waitUntil(dispatchViewInputResponse({ctx:t,deps:n,installationTeamId:l,metadata:i,submission:u,teamId:c,text:a})),r}async function dispatchViewInputResponse(e){let t=await authorizeInputResponse({channelId:e.metadata.channelId,deps:e.deps,installationTeamId:e.installationTeamId,submission:e.submission,teamId:e.teamId,threadTs:e.metadata.threadTs});if(t!==null){try{await e.ctx.from(e.metadata.continuationToken).respond(e.submission.inputResponses,{auth:t.auth})}catch(e){log.error(`freeform answer delivery failed`,{error:e});return}try{await updateAnsweredFreeformCard({channelId:e.metadata.messageChannelId??e.metadata.channelId,messageTs:e.metadata.messageTs,answerLabel:e.text,userId:e.submission.user.id,installationTeamId:e.installationTeamId,deps:e.deps})}catch(e){log.error(`freeform answered-card update failed`,{error:e})}}}export{handleInteractionPost,parseBlockActionsPayload,parseShortcutPayload};
1
+ import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#shared/input.js";import{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.js";import{isSlackResponseError}from"#public/channels/slack/transport.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildFreeformModalView,decodeFreeformHitlActionId,deriveHitlResponse,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{updateAnsweredFreeformCard,updateAnsweredHitlCard}from"#public/channels/slack/interaction-cards.js";import{approvalResponderStatePatch,authorizeInputResponse}from"#public/channels/slack/input-response.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{dispatchSlashCommand}from"#public/channels/slack/slash-command.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=s.team_id??o?.id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,installationTeamId:readInstallationTeamId(t),threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,installationTeamId:readInstallationTeamId(e.raw),threadTs:e.threadTs,teamId:e.user?.teamId??e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlocks(e)[0],n=readSlackTextObject(t?.text);return n.length>0?n:void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}function readInstallationTeamId(e){if(!isObjectRecord(e))return;let t=isObjectRecord(e.view)?e.view:void 0,n=isObjectRecord(e.team)?e.team:void 0,r=isObjectRecord(e.user)?e.user:void 0;return[t?.app_installed_team_id,e.app_installed_team_id,n?.id,r?.team_id,t?.team_id].find(e=>typeof e==`string`&&e.length>0)}async function handleInteractionPost(e,t,n){let r=new Response(`ok`,{status:200}),i;try{i=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),r}if(i.kind===`view_submission`)return handleViewSubmission(i,t,n);if(i.kind===`slash_command`)return dispatchSlashCommand(i,t,n),new Response(null,{status:200});if(i.kind===`unsupported`){let e=parseShortcutPayload(i.raw);return e===null?(log.warn(`unsupported Slack interaction payload ignored`,{type:i.type}),r):(dispatchShortcut(e,readInstallationTeamId(i.raw),t,n),new Response(null,{status:200}))}if(i.kind!==`block_actions`)return log.warn(`unsupported Slack interaction payload ignored`,{type:i.kind}),r;let a=parseBlockActionsPayload(i);if(!a)return r;let o=a.actions.find(e=>isFreeformAction(e.actionId));if(o)return await openFreeformModal({payload:i.raw,interaction:a,freeformAction:o,deps:n}),r;let s=a.actions.flatMap(e=>{let t=deriveHitlResponse(e);return t===null?[]:[{action:e,derived:t}]});if(s.length>0){let e=s[0].action.user,r=s[0].derived.route;t.waitUntil(dispatchBlockInputResponses({ctx:t,deps:n,interaction:a,route:r,submission:{type:`block_actions`,actions:s.map(({action:e})=>e),inputResponses:s.map(({derived:e})=>e.response),messageTs:s[0].action.messageTs,user:e}}))}let c=n.config.onInteraction;if(c){let e=a.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let r=e[0].user,{thread:i,slack:o}=buildSlackBinding({api:n.api,botToken:n.config.credentials?.botToken,channelId:a.channelId,threadTs:a.threadTs,installationTeamId:a.installationTeamId,teamId:a.teamId}),s={...bindSlackSessionOperations({address:slackContinuationToken(a.channelId,a.threadTs),defaultAuth:buildSlackAuthContext({channelId:a.channelId,teamId:a.teamId,threadTs:a.threadTs,userId:r.id,userName:r.username??r.name}),from:t.from,resolveSession:t.resolveSession,state:{channelId:a.channelId,installationTeamId:a.installationTeamId??null,teamId:a.teamId??null,threadTs:a.threadTs,triggeringUserId:r.id}}),thread:i,slack:o};for(let n of e)t.waitUntil(Promise.resolve(c(n,s)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return r}function parseShortcutPayload(e){if(!isObjectRecord(e))return null;let t=e.type;if(t!==`message_action`&&t!==`shortcut`)return null;let n=readRequiredString(e.callback_id),r=readRequiredString(e.trigger_id),i=isObjectRecord(e.user)?e.user:void 0,a=readRequiredString(i?.id);if(n===null||r===null||a===null)return null;let o=isObjectRecord(e.team)?e.team:void 0,s=readOptionalString(i?.team_id)??readOptionalString(o?.id),c={id:a,username:readOptionalString(i?.username),name:readOptionalString(i?.name)};if(t===`shortcut`)return{type:t,callbackId:n,triggerId:r,user:c,teamId:s};let l=isObjectRecord(e.channel)?e.channel:void 0,u=isObjectRecord(e.message)?e.message:void 0,d=readRequiredString(l?.id),f=readRequiredString(u?.ts);return d===null||f===null||u===void 0?null:{type:t,callbackId:n,triggerId:r,user:c,teamId:s,channelId:d,message:{text:typeof u.text==`string`?u.text:``,ts:f,threadTs:readOptionalString(u.thread_ts),userId:readOptionalString(u.user)},responseUrl:readOptionalString(e.response_url)}}function readRequiredString(e){return typeof e==`string`&&e.length>0?e:null}function readOptionalString(e){return typeof e==`string`&&e.length>0?e:void 0}function dispatchShortcut(e,t,n,r){let i=r.config.onShortcut;if(i===void 0){log.warn(`Slack shortcut ignored because onShortcut is not configured`,{type:e.type});return}let a={slack:buildSlackWorkspaceHandle({api:r.api,botToken:r.config.credentials?.botToken,installationTeamId:t,teamId:e.teamId})};dispatchInteractionHook(()=>i(e,a),n,`shortcut handler failed`)}function dispatchInteractionHook(e,t,n){t.waitUntil(Promise.resolve().then(e).catch(e=>{log.error(n,{error:e})}))}async function dispatchBlockInputResponses(e){let t=e.route?.channelId??e.interaction.channelId,n=e.route?.threadTs??e.interaction.threadTs,r=await authorizeInputResponse({channelId:t,deps:e.deps,installationTeamId:e.interaction.installationTeamId,submission:e.submission,teamId:e.interaction.teamId,threadTs:n});if(r!==null){try{await e.ctx.from(slackContinuationToken(t,n)).respond(e.submission.inputResponses,{auth:r.auth,state:approvalResponderStatePatch(e.submission,r.auth)})}catch(e){log.error(`HITL interaction delivery failed`,{error:e});return}if(!e.submission.actions.some(e=>deriveHitlResponse(e)?.kind===`tool-approval`))try{await updateAnsweredHitlCard(e.interaction,e.deps)}catch(e){log.error(`HITL answered-card update failed`,{error:e})}}}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=decodeFreeformHitlActionId(e.freeformAction.actionId);if(n===null){log.warn(`freeform button click carries invalid metadata`);return}let r=n.requestId;if(!r){log.warn(`freeform button click missing requestId`);return}let i=e.freeformAction.messageTs;if(!i){log.warn(`freeform button click missing messageTs`);return}let a=n?.route?.channelId??e.interaction.channelId,o=n?.route?.threadTs??e.interaction.threadTs,s={continuationToken:slackContinuationToken(a,o),channelId:a,installationTeamId:e.interaction.installationTeamId,messageChannelId:e.interaction.channelId,threadTs:o,messageTs:i,requestId:r},c=readPromptTextFromBlocks(e.interaction.messageBlocks),l=buildFreeformModalView({metadata:s,prompt:c}),u=await resolveSlackBotToken(e.deps.config.credentials?.botToken,{teamId:e.interaction.installationTeamId});try{await callSlackApi({api:e.deps.api,botToken:u,operation:`views.open`,body:{trigger_id:t,view:l}})}catch(e){if(!isSlackResponseError(e))throw e;log.error(`Slack views.open failed`,{error:e})}}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let i;try{i=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!i.continuationToken||!i.requestId||!i.messageTs||!i.channelId||!i.threadTs)return r;let a=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(a.length===0)return r;let o=e.user,s=e.userId,c=o?.teamId??e.teamId??null,l=i.installationTeamId??readInstallationTeamId(e.raw),u={type:`view_submission`,inputResponses:[parseInputResponse({requestId:i.requestId,text:a})],messageTs:i.messageTs,user:{id:s,username:o?.username,name:o?.name}};return t.waitUntil(dispatchViewInputResponse({ctx:t,deps:n,installationTeamId:l,metadata:i,submission:u,teamId:c,text:a})),r}async function dispatchViewInputResponse(e){let t=await authorizeInputResponse({channelId:e.metadata.channelId,deps:e.deps,installationTeamId:e.installationTeamId,submission:e.submission,teamId:e.teamId,threadTs:e.metadata.threadTs});if(t!==null){try{await e.ctx.from(e.metadata.continuationToken).respond(e.submission.inputResponses,{auth:t.auth})}catch(e){log.error(`freeform answer delivery failed`,{error:e});return}try{await updateAnsweredFreeformCard({channelId:e.metadata.messageChannelId??e.metadata.channelId,messageTs:e.metadata.messageTs,answerLabel:e.text,userId:e.submission.user.id,installationTeamId:e.installationTeamId,deps:e.deps})}catch(e){log.error(`freeform answered-card update failed`,{error:e})}}}export{handleInteractionPost,parseBlockActionsPayload,parseShortcutPayload};
@@ -8,6 +8,7 @@ import type { ChannelContinuationOps } from "#public/definitions/channel.js";
8
8
  import type { UnstampedMessageStreamEvent } from "#protocol/message.js";
9
9
  import type { InputRequest, InputResponse, StrictInputResponses, ValidatedInputResponse } from "#shared/input.js";
10
10
  import { type SlackBotToken, type SlackHandle, type SlackThread, type SlackWorkspaceHandle } from "#public/channels/slack/api.js";
11
+ import { type SlackTransportOptions } from "#public/channels/slack/transport.js";
11
12
  import { type SlackEvent, type SlackEventEnvelope, type SlackMessage } from "#public/channels/slack/inbound.js";
12
13
  import { type LoadThreadContextMessagesOptions } from "#public/channels/slack/thread.js";
13
14
  import { type SlackSessionOperations } from "#public/channels/slack/session-operations.js";
@@ -490,6 +491,8 @@ export type SlackApprovalRequest = InputRequest;
490
491
  /** Chooses the Slack destination for one input request. */
491
492
  export type SlackApprovalChannelResolver = (request: SlackApprovalRequest, ctx: SessionContext) => SlackApprovalChannel | Promise<SlackApprovalChannel>;
492
493
  export interface SlackChannelConfig {
494
+ /** Slack API transport overrides: base URLs and a custom fetch. */
495
+ readonly api?: SlackTransportOptions;
493
496
  readonly credentials?: SlackChannelCredentials;
494
497
  readonly botName?: string;
495
498
  /**
@@ -1 +1 @@
1
- import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{normalizeChannelAudience}from"#shared/channel-audience.js";import{setChannelActivityRenderers}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,postCompletedSlackReply}from"#public/channels/slack/defaults.js";import{buildSlackActivityRenderers,hasSlackActivityStatus}from"#public/channels/slack/activity.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackEventReceivingBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`),ignoreSlackActivityEvent=async()=>void 0,activityOwnedMessageCompleted=async(e,t)=>{t.state.pendingToolCallMessage=null,e.finishReason!==`tool-calls`&&e.message&&await postCompletedSlackReply(t,e.message)},clearSlackTurnState=async(e,t)=>{t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null};function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.alias(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),r=buildSlackActivityRenderers({botToken:e.credentials?.botToken,renderers:e.activity?.renderers??[]}),i=e.onInputResponse??defaultOnInputResponse,a=e.events?.[`authorization.required`],o=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],s=hasSlackActivityStatus(e.activity?.renderers),c=e.events?.[`turn.started`]??(s?clearSlackTurnState:defaultEvents[`turn.started`]),l=e.events?.[`reasoning.appended`]??(s?ignoreSlackActivityEvent:defaultEvents[`reasoning.appended`]),u=e.events?.[`actions.requested`]??(s?ignoreSlackActivityEvent:defaultEvents[`actions.requested`]),d=e.events?.[`message.completed`]??(s?activityOwnedMessageCompleted:defaultEvents[`message.completed`]),f=defaultInputRequestedHandler(e.approvalChannel),p=e.events?.[`input.requested`],m=p===void 0?f:async(e,t,n)=>p(e,t,n,async e=>{await f(e,t,n)}),h={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await o(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await c(e,t,n)},"reasoning.appended":l,"actions.requested":u,"message.completed":d,"input.requested":m,"authorization.required":a===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(a)},g=new Set,_=defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{channelId:null,threadTs:null,teamId:null,installationTeamId:null,triggeringUserId:null,triggeringMessageTs:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},audience:({state:e})=>e.audience??`unknown`,context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let r=e.state?.approvalResponderUsers;return typeof r==`object`&&r&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...r}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:a}=r,{from:o,resolveSession:s}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:o,resolveSession:s,waitUntil:a},{config:e,onInputResponse:i}):handleEventPost({body:c,from:o,resolveSession:s,waitUntil:a,config:e,uploadPolicy:t,handledEvents:g,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:h});return setChannelActivityRenderers(_,{destination(e){let t=e;return{channelId:t?.channelId??null,installationTeamId:t?.installationTeamId??null,teamId:t?.teamId??null,threadTs:t?.threadTs??null,triggeringUserId:t?.triggeringUserId??null}},renderers:r}),_}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,o=n.initialMessage;if(o&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let s=i;if(o){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:a,teamId:t.teamId}),n={card:o.card};o.fallbackText!==void 0&&(n.fallbackText=o.fallbackText),s=(await e.post(n)).id}let c=s||crypto.randomUUID(),l=n.audience===void 0?void 0:normalizeChannelAudience(n.audience),u={channelId:r,installationTeamId:a??null,threadTs:s||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null};return l!==void 0&&(u.audience=l),t.from(slackContinuationToken(r,c)).send(e.message,{auth:e.auth,state:u,title:e.title,taskDeliveryPolicy:e.taskDeliveryPolicy})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(e){let{config:t}=e,n,r;try{n=parseSlackWebhookBody(e.body,{headers:e.headers}),r=parseSlackEventEnvelope(e.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(n.kind===`url_verification`)return new Response(n.challenge,{status:200,headers:{"content-type":`text/plain`}});if(r===null)return new Response(`ok`);let i=typeof r.api_app_id==`string`?r.api_app_id:void 0,a=slackEventBotUserId(r),o=slackEventReceivingBotUserId(r),s=slackEventInstallationTeamId(r),dispatch=null,c=null;if(n.kind===`app_mention`||n.kind===`direct_message`){let r=n.kind,l=slackMessageFromWebhookPayload(n);if(l!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},l)){let dispatchMessageWith=n=>()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}),n=(r===`app_mention`?t.onAppMention:t.onDirectMessage)??t.onMessage;n===void 0?c=dispatchMessageWith(r===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy})}}if(dispatch===null&&t.onMessage!==void 0){let n=parseMessageEvent(r);n!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},n)&&(a===void 0||!n.text.includes(`<@${a}`))&&(dispatch=()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:t.onMessage,installationTeamId:s,kind:`channel_message`,message:n,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}))}let l=t.onEvent;if(dispatch===null&&l!==void 0&&(dispatch=()=>dispatchSlackEvent({from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,envelope:r,handler:l,installationTeamId:s})),dispatch??=c,dispatch===null)return new Response(`ok`);let u=r.event_id;if(u){if(e.handledEvents.has(u))return log.warn(`received a duplicate event`,{event_id:u,event_time:r.event_time,retry_num:n.retry?.num??`(null)`,retry_reason:n.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(u,e.handledEvents)}return e.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:n,slack:r}=buildSlackBinding({appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,installationTeamId:e.installationTeamId,teamId:e.message.teamId}),i=e.message.author,a={channelId:e.message.channelId,installationTeamId:e.installationTeamId??null,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringMessageTs:e.message.ts,triggeringUserId:i?.userId??null},o=bindSlackSessionOperations({address:t,defaultAuth:i===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:i.fullName,isBot:i.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:i.userId,userName:i.userName}),from:e.from,resolveSession:e.resolveSession,state:a}),s,isDMOrPrivateChannel=()=>s??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:r.request}),c=e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),l={...o,isBotMentioned:()=>c,isDMOrPrivateChannel,isSubscribed:async()=>await o.resolveSession()!==void 0,slack:r,thread:n},u;try{u=await e.handler(l,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(u==null)return;let d=await isDMOrPrivateChannel();a.audience=e.kind===`direct_message`||d?`private`:`public`,await deliverSlackMessage({botUserId:e.receivingBotUserId,credentials:e.credentials,kind:e.kind,isPrivateConversation:d,isMentioned:c,message:e.message,result:u,sessionOperations:o,thread:n,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,n=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,r=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),i={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:r,target:i,title:a,taskDeliveryPolicy:o})=>receiveOnSlack({auth:r,message:t,target:i,title:a,taskDeliveryPolicy:o},{from:e.from,credentials:e.credentials,installationTeamId:e.installationTeamId,teamId:n,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:n}),waitUntil(e){r.push(e)}};try{await e.handler(i,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(r)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:n}=e;try{let r=e.threadContext===void 0?[]:await loadThreadContextMessages(n,t,e.threadContext),i=formatSlackThreadContext(r),a=await collectInboundFileParts({mention:t,thread:n,policy:e.uploadPolicy}),o={botUserId:e.botUserId,channelId:t.channelId,fullName:t.author?.fullName,isMentioned:e.isMentioned,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},s=formatSlackInboundMessage(o,t),c=buildSlackTurnMessage(i===void 0?s:`${i}\n\n${s}`,a),l=e.result.context??[],u=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,d=l.length===0?{auth:e.result.auth,title:u}:{auth:e.result.auth,context:l,title:u};await e.sessionOperations.send(c,d)}catch(n){logError(log,`${e.kind} delivery failed`,n,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
1
+ import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{normalizeChannelAudience}from"#shared/channel-audience.js";import{setChannelActivityRenderers}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{resolveSlackTransportOptions}from"#public/channels/slack/transport.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,postCompletedSlackReply}from"#public/channels/slack/defaults.js";import{buildSlackActivityRenderers,hasSlackActivityStatus}from"#public/channels/slack/activity.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackEventReceivingBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`),ignoreSlackActivityEvent=async()=>void 0,activityOwnedMessageCompleted=async(e,t)=>{t.state.pendingToolCallMessage=null,e.finishReason!==`tool-calls`&&e.message&&await postCompletedSlackReply(t,e.message)},clearSlackTurnState=async(e,t)=>{t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null};function rebuildSlackContext(e,t,n,r){let{thread:i,slack:a}=buildSlackBinding({api:n,botToken:r?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.alias(slackContinuationToken(e.channelId,n))}});return{thread:i,slack:a,state:e}}function slackChannel(e={}){let t=resolveSlackTransportOptions(e.api),n=mergeUploadPolicy(e.uploadPolicy),r=createSlackFetchFile({api:t,botToken:e.credentials?.botToken}),i=buildSlackActivityRenderers({api:t,botToken:e.credentials?.botToken,renderers:e.activity?.renderers??[]}),a=e.onInputResponse??defaultOnInputResponse,o=e.events?.[`authorization.required`],s=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],c=hasSlackActivityStatus(e.activity?.renderers),l=e.events?.[`turn.started`]??(c?clearSlackTurnState:defaultEvents[`turn.started`]),u=e.events?.[`reasoning.appended`]??(c?ignoreSlackActivityEvent:defaultEvents[`reasoning.appended`]),d=e.events?.[`actions.requested`]??(c?ignoreSlackActivityEvent:defaultEvents[`actions.requested`]),f=e.events?.[`message.completed`]??(c?activityOwnedMessageCompleted:defaultEvents[`message.completed`]),p=defaultInputRequestedHandler(e.approvalChannel),m=e.events?.[`input.requested`],h=m===void 0?p:async(e,t,n)=>m(e,t,n,async e=>{await p(e,t,n)}),g={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await s(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await l(e,t,n)},"reasoning.appended":u,"actions.requested":d,"message.completed":f,"input.requested":h,"authorization.required":o===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(o)},_=new Set,v=defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{channelId:null,threadTs:null,teamId:null,installationTeamId:null,triggeringUserId:null,triggeringMessageTs:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:r,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},audience:({state:e})=>e.audience??`unknown`,context(n,r){return rebuildSlackContext(n,r,t,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let r=e.state?.approvalResponderUsers;return typeof r==`object`&&r&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...r}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(r,i)=>{let{waitUntil:o}=i,{from:s,resolveSession:c}=i,l=await verifyInbound(r,e.credentials);return l===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(r.headers)?new Response(`ok`):(r.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(l,{from:s,resolveSession:c,waitUntil:o},{api:t,config:e,onInputResponse:a}):handleEventPost({api:t,body:l,from:s,resolveSession:c,waitUntil:o,config:e,uploadPolicy:n,handledEvents:_,headers:r.headers})})],receive(n,{from:r}){return receiveOnSlack(n,{from:r,api:t,credentials:e.credentials})},events:g});return setChannelActivityRenderers(v,{destination(e){let t=e;return{channelId:t?.channelId??null,installationTeamId:t?.installationTeamId??null,teamId:t?.teamId??null,threadTs:t?.threadTs??null,triggeringUserId:t?.triggeringUserId??null}},renderers:i}),v}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,o=n.initialMessage;if(o&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let s=i;if(o){let{thread:e}=buildSlackBinding({api:t.api,botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:a,teamId:t.teamId}),n={card:o.card};o.fallbackText!==void 0&&(n.fallbackText=o.fallbackText),s=(await e.post(n)).id}let c=s||crypto.randomUUID(),l=n.audience===void 0?void 0:normalizeChannelAudience(n.audience),u={channelId:r,installationTeamId:a??null,threadTs:s||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null};return l!==void 0&&(u.audience=l),t.from(slackContinuationToken(r,c)).send(e.message,{auth:e.auth,state:u,title:e.title,taskDeliveryPolicy:e.taskDeliveryPolicy})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(e){let{config:t}=e,n,r;try{n=parseSlackWebhookBody(e.body,{headers:e.headers}),r=parseSlackEventEnvelope(e.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(n.kind===`url_verification`)return new Response(n.challenge,{status:200,headers:{"content-type":`text/plain`}});if(r===null)return new Response(`ok`);let i=typeof r.api_app_id==`string`?r.api_app_id:void 0,a=slackEventBotUserId(r),o=slackEventReceivingBotUserId(r),s=slackEventInstallationTeamId(r),dispatch=null,c=null;if(n.kind===`app_mention`||n.kind===`direct_message`){let r=n.kind,l=slackMessageFromWebhookPayload(n);if(l!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},l)){let dispatchMessageWith=n=>()=>dispatchSlackMessage({api:e.api,appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}),n=(r===`app_mention`?t.onAppMention:t.onDirectMessage)??t.onMessage;n===void 0?c=dispatchMessageWith(r===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({api:e.api,appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy})}}if(dispatch===null&&t.onMessage!==void 0){let n=parseMessageEvent(r);n!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},n)&&(a===void 0||!n.text.includes(`<@${a}`))&&(dispatch=()=>dispatchSlackMessage({api:e.api,appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:t.onMessage,installationTeamId:s,kind:`channel_message`,message:n,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}))}let l=t.onEvent;if(dispatch===null&&l!==void 0&&(dispatch=()=>dispatchSlackEvent({api:e.api,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,envelope:r,handler:l,installationTeamId:s})),dispatch??=c,dispatch===null)return new Response(`ok`);let u=r.event_id;if(u){if(e.handledEvents.has(u))return log.warn(`received a duplicate event`,{event_id:u,event_time:r.event_time,retry_num:n.retry?.num??`(null)`,retry_reason:n.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(u,e.handledEvents)}return e.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:n,slack:r}=buildSlackBinding({api:e.api,appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,installationTeamId:e.installationTeamId,teamId:e.message.teamId}),i=e.message.author,a={channelId:e.message.channelId,installationTeamId:e.installationTeamId??null,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringMessageTs:e.message.ts,triggeringUserId:i?.userId??null},o=bindSlackSessionOperations({address:t,defaultAuth:i===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:i.fullName,isBot:i.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:i.userId,userName:i.userName}),from:e.from,resolveSession:e.resolveSession,state:a}),s,isDMOrPrivateChannel=()=>s??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:r.request}),c=e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),l={...o,isBotMentioned:()=>c,isDMOrPrivateChannel,isSubscribed:async()=>await o.resolveSession()!==void 0,slack:r,thread:n},u;try{u=await e.handler(l,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(u==null)return;let d=await isDMOrPrivateChannel();a.audience=e.kind===`direct_message`||d?`private`:`public`,await deliverSlackMessage({botUserId:e.receivingBotUserId,credentials:e.credentials,kind:e.kind,isPrivateConversation:d,isMentioned:c,message:e.message,result:u,sessionOperations:o,thread:n,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,n=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,r=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),i={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:r,target:i,title:a,taskDeliveryPolicy:o})=>receiveOnSlack({auth:r,message:t,target:i,title:a,taskDeliveryPolicy:o},{from:e.from,api:e.api,credentials:e.credentials,installationTeamId:e.installationTeamId,teamId:n,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({api:e.api,botToken:e.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:n}),waitUntil(e){r.push(e)}};try{await e.handler(i,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(r)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:n}=e;try{let r=e.threadContext===void 0?[]:await loadThreadContextMessages(n,t,e.threadContext),i=formatSlackThreadContext(r),a=await collectInboundFileParts({mention:t,thread:n,policy:e.uploadPolicy}),o={botUserId:e.botUserId,channelId:t.channelId,fullName:t.author?.fullName,isMentioned:e.isMentioned,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},s=formatSlackInboundMessage(o,t),c=buildSlackTurnMessage(i===void 0?s:`${i}\n\n${s}`,a),l=e.result.context??[],u=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,d=l.length===0?{auth:e.result.auth,title:u}:{auth:e.result.auth,context:l,title:u};await e.sessionOperations.send(c,d)}catch(n){logError(log,`${e.kind} delivery failed`,n,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
@@ -1,9 +1,13 @@
1
1
  import { type parseSlackWebhookBody } from "#compiled/@chat-adapter/slack/webhook.js";
2
2
  import type { SlackChannelConfig } from "#public/channels/slack/slackChannel.js";
3
+ import type { SlackTransportOptions } from "#public/channels/slack/transport.js";
3
4
  type SlashCommandPayload = Extract<ReturnType<typeof parseSlackWebhookBody>, {
4
5
  kind: "slash_command";
5
6
  }>;
6
7
  export declare function dispatchSlashCommand(payload: SlashCommandPayload, ctx: {
7
8
  readonly waitUntil: (task: Promise<unknown>) => void;
8
- }, config: SlackChannelConfig): void;
9
+ }, deps: {
10
+ readonly api: SlackTransportOptions | undefined;
11
+ readonly config: SlackChannelConfig;
12
+ }): void;
9
13
  export {};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{buildSlackWorkspaceHandle}from"#public/channels/slack/api.js";import"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.slash-command`);function dispatchSlashCommand(e,t,n){let r=parseSlashCommandPayload(e),i=n.onSlashCommand;if(i===void 0){log.warn(`Slack slash command ignored because onSlashCommand is not configured`,{command:r.command});return}let a={slack:buildSlackWorkspaceHandle({botToken:n.credentials?.botToken,installationTeamId:r.teamId,teamId:r.teamId})};t.waitUntil(Promise.resolve().then(()=>i(r,a)).catch(e=>{log.error(`slash command handler failed`,{error:e})}))}function parseSlashCommandPayload(e){return{command:e.command,text:e.text,user:{id:e.userId,username:e.userName},teamId:e.teamId,channelId:e.channelId,channelName:e.channelName,enterpriseId:e.enterpriseId,isEnterpriseInstall:e.isEnterpriseInstall,triggerId:e.triggerId,responseUrl:e.responseUrl}}export{dispatchSlashCommand};
1
+ import{createLogger}from"#internal/logging.js";import{buildSlackWorkspaceHandle}from"#public/channels/slack/api.js";import"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.slash-command`);function dispatchSlashCommand(e,t,n){let r=parseSlashCommandPayload(e),i=n.config.onSlashCommand;if(i===void 0){log.warn(`Slack slash command ignored because onSlashCommand is not configured`,{command:r.command});return}let a={slack:buildSlackWorkspaceHandle({api:n.api,botToken:n.config.credentials?.botToken,installationTeamId:r.teamId,teamId:r.teamId})};t.waitUntil(Promise.resolve().then(()=>i(r,a)).catch(e=>{log.error(`slash command handler failed`,{error:e})}))}function parseSlashCommandPayload(e){return{command:e.command,text:e.text,user:{id:e.userId,username:e.userName},teamId:e.teamId,channelId:e.channelId,channelName:e.channelName,enterpriseId:e.enterpriseId,isEnterpriseInstall:e.isEnterpriseInstall,triggerId:e.triggerId,responseUrl:e.responseUrl}}export{dispatchSlashCommand};
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The seam between eve and the vendored Slack transport: the host and fetch
3
+ * every outbound call travels on, and the record of whether a thrown error
4
+ * means Slack answered.
5
+ */
6
+ import { type SlackApiOptions, type SlackApiResponse } from "#compiled/@chat-adapter/slack/api.js";
7
+ /** Fetch implementation used for Slack traffic. */
8
+ export type SlackFetch = typeof globalThis.fetch;
9
+ /**
10
+ * Slack API transport overrides. The base URLs say where a call may go:
11
+ * `apiBaseUrl` defaults to `https://slack.com/api/`, and `fileBaseUrl` falls back to
12
+ * `apiBaseUrl` for attachment downloads. Both are absolute http or https URLs
13
+ * carrying no query string or fragment.
14
+ *
15
+ * `fetch` says how the traffic travels: it replaces the global fetch, which attaches
16
+ * the per-call header a host behind an authenticating proxy requires, and observes or
17
+ * stubs the calls. It is confined to the configured bases' origins, so a wrapper may
18
+ * attach a credential without checking each destination itself. With no base
19
+ * configured every call goes to Slack's own hosts and it sees all of them.
20
+ */
21
+ export interface SlackTransportOptions {
22
+ readonly apiBaseUrl?: string;
23
+ readonly fetch?: SlackFetch;
24
+ readonly fileBaseUrl?: string;
25
+ }
26
+ /**
27
+ * Checks the configured bases and resolves each to the directory href that method
28
+ * names and file paths resolve against, throwing on one eve cannot reach.
29
+ * `fileBaseUrl` absorbs its fallback here, `fetch` is confined here, and every
30
+ * outbound call then reads a resolved string.
31
+ *
32
+ * Resolving an already-resolved value returns it unchanged, so `slackChannel()`
33
+ * can resolve once at construction — where a bad base belongs — while an entry
34
+ * point reached without a channel resolves for itself.
35
+ */
36
+ export declare function resolveSlackTransportOptions(api: SlackTransportOptions | undefined): SlackTransportOptions | undefined;
37
+ /**
38
+ * The options an outbound Slack Web API call is made with. Resolves `api`, so a
39
+ * caller cannot reach the transport with a base that was never checked or a fetch
40
+ * that was never confined.
41
+ */
42
+ export declare function slackApiOptions(api: SlackTransportOptions | undefined, token: SlackApiOptions["token"]): SlackApiOptions;
43
+ /**
44
+ * True when a configured file base serves `url`, which makes it downloadable with
45
+ * the bot token. The match is a path prefix, so an `apiBaseUrl` of
46
+ * `https://slack.com/api/` authorizes downloads under that path alone.
47
+ */
48
+ export declare function isConfiguredSlackFileUrl(api: SlackTransportOptions | undefined, url: string): boolean;
49
+ /**
50
+ * POSTs to a Slack Web API method and records the response arriving.
51
+ *
52
+ * Slack retries an interaction whose acknowledgement is not a 2xx, so a
53
+ * handler answering a click has to swallow a call Slack answered badly
54
+ * and rethrow a call that never arrived. Those two are indistinguishable
55
+ * once thrown: the transport consumes the response body before it reads
56
+ * the status, so a body cut off partway raises the same `TypeError` as a
57
+ * connection that was never made. Nothing on the error says which it
58
+ * was, so the answer is recorded here while the call runs. `callSlackApi`
59
+ * posts through here, which covers `ctx.slack.request` and the
60
+ * `views.open` that opens the freeform modal; the message, upload and
61
+ * thread-fetch helpers in `api.ts` post through the vendored transport.
62
+ *
63
+ * The transport is handed a `fetch` that observes the response, so
64
+ * everything thrown from that moment on — a non-2xx, a body that is not
65
+ * the JSON envelope, a body cut off mid-read — is marked for
66
+ * {@link isSlackResponseError}. A failure before then stays unmarked and
67
+ * reaches the caller, which is what fails the interaction request.
68
+ */
69
+ export declare function callSlackApiTrackingResponse(operation: string, body: Record<string, unknown>, options: SlackApiOptions): Promise<SlackApiResponse>;
70
+ /**
71
+ * True when Slack answered the call, false when the call never got a
72
+ * response. A caller that must acknowledge Slack swallows the first and
73
+ * rethrows the second; see {@link callSlackApiTrackingResponse} for why
74
+ * the error itself cannot be asked.
75
+ */
76
+ export declare function isSlackResponseError(error: unknown): boolean;
@@ -0,0 +1 @@
1
+ import{callSlackApi}from"#compiled/@chat-adapter/slack/api.js";const resolvedTransports=new WeakSet;function resolveSlackTransportOptions(e){if(e===void 0)return;if(resolvedTransports.has(e))return e;let t=slackBaseHref(e.apiBaseUrl,`api.apiBaseUrl`),n=e.fileBaseUrl===void 0?t:slackBaseHref(e.fileBaseUrl,`api.fileBaseUrl`),r={apiBaseUrl:t,fetch:confineFetch(e.fetch,t,n),fileBaseUrl:n};return resolvedTransports.add(r),r}function confineFetch(e,t,n){if(e===void 0)return;let r=[t,n].filter(e=>e!==void 0);if(r.length===0)return e;let i=new Set(r.map(e=>new URL(e).origin));return(t,n)=>{let r=URL.parse(t instanceof Request?t.url:String(t))?.origin;return r!==void 0&&i.has(r)?e(t,n):globalThis.fetch(t,n)}}function slackApiOptions(e,t){let n=resolveSlackTransportOptions(e);return{apiUrl:n?.apiBaseUrl,fetch:n?.fetch,token:t}}function isConfiguredSlackFileUrl(e,t){let n=e?.fileBaseUrl;return n!==void 0&&URL.parse(t)?.href.startsWith(n)===!0}function slackBaseHref(e,t){if(e===void 0)return;let n=URL.parse(e);if(n===null||n.protocol!==`http:`&&n.protocol!==`https:`)throw TypeError(`${t} must be an absolute http or https URL. Received: ${e}`);if(n.search!==``||n.hash!==``)throw TypeError(`${t} must carry no query string or fragment. Received: ${e}`);return n.href.endsWith(`/`)?n.href:`${n.href}/`}const slackResponseErrors=new WeakSet;async function callSlackApiTrackingResponse(e,t,n){let r=!1,observe=async(e,t)=>{let i=await(n.fetch??globalThis.fetch)(e,t);return r=!0,i};try{return await callSlackApi(e,t,{...n,fetch:observe})}catch(e){throw r&&typeof e==`object`&&e&&slackResponseErrors.add(e),e}}function isSlackResponseError(e){return typeof e==`object`&&!!e&&slackResponseErrors.has(e)}export{callSlackApiTrackingResponse,isConfiguredSlackFileUrl,isSlackResponseError,resolveSlackTransportOptions,slackApiOptions};
@@ -22,13 +22,6 @@ export interface ChannelSetupChoice {
22
22
  }
23
23
  /** Optional interaction capability for a long-running setup operation. */
24
24
  export type ChannelSetupAwaitChoice = (options: ChannelSetupChoiceOptions) => ChannelSetupChoice;
25
- /** Why a live setup indicator is waiting. */
26
- export type SetupSpinnerIntent = {
27
- kind: "progress";
28
- } | {
29
- kind: "external-action";
30
- emphasis: string;
31
- };
32
25
  /** Status and subprocess output operations used by shared setup flows. */
33
26
  export interface ChannelSetupLog {
34
27
  message(text: string): void;
@@ -45,7 +38,7 @@ export interface ChannelSetupLog {
45
38
  * (which have no live status surface) can omit it and callers fall back to
46
39
  * a persisted message.
47
40
  */
48
- spinner?(message: string, intent?: SetupSpinnerIntent): {
41
+ spinner?(message: string): {
49
42
  stop(): void;
50
43
  };
51
44
  }
@@ -55,4 +48,4 @@ export interface ChannelSetupLog {
55
48
  * phase message instead, keeping their progress trail; the spinner stops
56
49
  * whether the work resolves or throws.
57
50
  */
58
- export declare function withPhase<T>(log: ChannelSetupLog, message: string, task: () => Promise<T>, intent?: SetupSpinnerIntent): Promise<T>;
51
+ export declare function withPhase<T>(log: ChannelSetupLog, message: string, task: () => Promise<T>): Promise<T>;
@@ -1 +1 @@
1
- async function withPhase(e,t,n,r){let i=e.spinner?.(t,r);i||e.message(t);try{return await n()}finally{i?.stop()}}export{withPhase};
1
+ async function withPhase(e,t,n){let r=e.spinner?.(t);r||e.message(t);try{return await n()}finally{r?.stop()}}export{withPhase};
@@ -1,5 +1,5 @@
1
1
  export { type ConnectionSelectOption } from "./connection-add-prompter.js";
2
- export { type ChannelSetupAction, type ChannelSetupAwaitChoice, type ChannelSetupChoice, type ChannelSetupChoiceOptions, type ChannelSetupLog, type DisabledChannelReasons, type SetupSpinnerIntent, withPhase, } from "./channel-setup-prompter.js";
2
+ export { type ChannelSetupAction, type ChannelSetupAwaitChoice, type ChannelSetupChoice, type ChannelSetupChoiceOptions, type ChannelSetupLog, type DisabledChannelReasons, withPhase, } from "./channel-setup-prompter.js";
3
3
  export { createPromptCommandOutput, type PromptCommandLog } from "./command-output.js";
4
4
  export { runSelectComponent, SelectComponent, type SelectGuard } from "./select-component.js";
5
5
  export { createSelectOptionCodec, type SelectOptionCodec } from "./select-option-codec.js";
@@ -1,3 +1,3 @@
1
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,t=`user`){let n=parseJson(e),r=parseConnectorRef(n);if(!isRecord(n)||r===void 0)return;let i=n.supportedSubjectTypes;return Array.isArray(i)&&i.includes(t)?r: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,t,n){let r=[],i=new Set,a;do{let o=[`connect`,`list`,`-F`,`json`,`--all-projects`,`--service`,e.service,`--scope`,t.orgId];a!==void 0&&o.push(`--next`,a);let s=await captureVercel(o,{cwd:e.projectRoot,onOutput:n,signal:e.signal});if(!s.ok)throw Error(s.failure.message);let c=parseConnectorListPage(parseJson(s.stdout),e.service);if(c===void 0)throw Error(`Vercel returned an invalid connector list for ${e.service}.`);r.push(...c.connectors);let l=c.cursor;if(l!==void 0&&i.has(l))throw Error(`The connector list repeated cursor ${l}.`);l!==void 0&&i.add(l),a=l}while(a!==void 0);return r}async function supportsPrincipalType(e,t,n,r){let i=`/v1/connect/connectors/${encodeURIComponent(n.id)}`,a=await captureVercel([`api`,i,`--scope`,t.orgId,`--raw`],{cwd:e.projectRoot,onOutput:r,signal:e.signal});if(!a.ok)throw Error(`Could not verify connector ${n.uid}.`);let o=parseJson(a.stdout);if(!isRecord(o)||o.id!==n.id||o.uid!==n.uid||typeof o.service==`string`&&o.service!==e.service)throw Error(`Vercel returned invalid details for connector ${n.uid}.`);let s=o.supportedSubjectTypes;return Array.isArray(s)&&s.includes(e.principalType??`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(e){let t=e.orgId??(await readProjectLink(e.projectRoot))?.orgId,n=[`connect`,`remove`,e.connectorId,`--disconnect-all`,`--yes`];if(t!==void 0&&n.push(`--scope`,t),!await runVercel(n,{cwd:e.projectRoot,onOutput:createPromptCommandOutput(e.log)}))throw Error(`Could not remove connector ${e.connectorId}; run \`vercel connect remove ${e.connectorId} --disconnect-all --yes\`.`)}async function attach(e,t,n,r){return runVercel([`connect`,`attach`,n,`--yes`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:r,signal:e.signal})}async function resolveFallbackConnector(e,t,n,r,i){let a=e.principalType??`user`,o=i;for(;;){if(await e.prompter.select({message:`Which connector should ${e.slug} use?`,hintLayout:`inline`,notices:[{tone:`warning`,text:o}],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 i=[];for(let a of r)await supportsPrincipalType(e,t,a,n)&&i.push(a);if(i.length===0){o=`No existing ${e.service} connectors support ${a} credentials.`;continue}let s=new Map(i.map(e=>[e.uid,e])),c=await e.prompter.select({message:`Select a connector for ${e.slug}`,hintLayout:`inline`,search:!0,placeholder:`type to search connectors`,options:i.map(e=>({value:e.uid,label:e.uid,hint:e.name??e.id}))}),l=s.get(c);if(l===void 0)throw Error(`Connector ${c} is no longer available.`);return{kind:`existing`,connector:l}}let i=connectorNames(r),s=(await e.prompter.text({message:`New connector name`,defaultValue:nextConnectorName(e.slug,i),validate:e=>{let t=e.trim().toLowerCase();return t.length===0?`A name is required.`:i.has(t)?`A connector with this name already exists.`:void 0}})).trim(),c=[],createOutput=e=>{c.push(e.text),n(e)},l=await withPhase(e.log,`Waiting for you to complete setup in the browser…`,()=>runVercelCaptureStdout([`connect`,`create`,e.creationType??e.service,`--name`,s,...e.connectionMethod===void 0?[]:[`--connection-method`,e.connectionMethod],`-F`,`json`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:createOutput,signal:e.signal}),{kind:`external-action`,emphasis:`browser`}),u=parseConnectorRef(parseJson(l.stdout))?.id??CREATED_CONNECTOR.exec(c.join(`
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,t=`user`){let n=parseJson(e),r=parseConnectorRef(n);if(!isRecord(n)||r===void 0)return;let i=n.supportedSubjectTypes;return Array.isArray(i)&&i.includes(t)?r: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,t,n){let r=[],i=new Set,a;do{let o=[`connect`,`list`,`-F`,`json`,`--all-projects`,`--service`,e.service,`--scope`,t.orgId];a!==void 0&&o.push(`--next`,a);let s=await captureVercel(o,{cwd:e.projectRoot,onOutput:n,signal:e.signal});if(!s.ok)throw Error(s.failure.message);let c=parseConnectorListPage(parseJson(s.stdout),e.service);if(c===void 0)throw Error(`Vercel returned an invalid connector list for ${e.service}.`);r.push(...c.connectors);let l=c.cursor;if(l!==void 0&&i.has(l))throw Error(`The connector list repeated cursor ${l}.`);l!==void 0&&i.add(l),a=l}while(a!==void 0);return r}async function supportsPrincipalType(e,t,n,r){let i=`/v1/connect/connectors/${encodeURIComponent(n.id)}`,a=await captureVercel([`api`,i,`--scope`,t.orgId,`--raw`],{cwd:e.projectRoot,onOutput:r,signal:e.signal});if(!a.ok)throw Error(`Could not verify connector ${n.uid}.`);let o=parseJson(a.stdout);if(!isRecord(o)||o.id!==n.id||o.uid!==n.uid||typeof o.service==`string`&&o.service!==e.service)throw Error(`Vercel returned invalid details for connector ${n.uid}.`);let s=o.supportedSubjectTypes;return Array.isArray(s)&&s.includes(e.principalType??`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(e){let t=e.orgId??(await readProjectLink(e.projectRoot))?.orgId,n=[`connect`,`remove`,e.connectorId,`--disconnect-all`,`--yes`];if(t!==void 0&&n.push(`--scope`,t),!await runVercel(n,{cwd:e.projectRoot,onOutput:createPromptCommandOutput(e.log)}))throw Error(`Could not remove connector ${e.connectorId}; run \`vercel connect remove ${e.connectorId} --disconnect-all --yes\`.`)}async function attach(e,t,n,r){return runVercel([`connect`,`attach`,n,`--yes`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:r,signal:e.signal})}async function resolveFallbackConnector(e,t,n,r,i){let a=e.principalType??`user`,o=i;for(;;){if(await e.prompter.select({message:`Which connector should ${e.slug} use?`,hintLayout:`inline`,notices:[{tone:`warning`,text:o}],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 i=[];for(let a of r)await supportsPrincipalType(e,t,a,n)&&i.push(a);if(i.length===0){o=`No existing ${e.service} connectors support ${a} credentials.`;continue}let s=new Map(i.map(e=>[e.uid,e])),c=await e.prompter.select({message:`Select a connector for ${e.slug}`,hintLayout:`inline`,search:!0,placeholder:`type to search connectors`,options:i.map(e=>({value:e.uid,label:e.uid,hint:e.name??e.id}))}),l=s.get(c);if(l===void 0)throw Error(`Connector ${c} is no longer available.`);return{kind:`existing`,connector:l}}let i=connectorNames(r),s=(await e.prompter.text({message:`New connector name`,defaultValue:nextConnectorName(e.slug,i),validate:e=>{let t=e.trim().toLowerCase();return t.length===0?`A name is required.`:i.has(t)?`A connector with this name already exists.`:void 0}})).trim(),c=[],createOutput=e=>{c.push(e.text),n(e)},l=await withPhase(e.log,`Waiting for you to complete setup in the browser…`,()=>runVercelCaptureStdout([`connect`,`create`,e.creationType??e.service,`--name`,s,...e.connectionMethod===void 0?[]:[`--connection-method`,e.connectionMethod],`-F`,`json`,`--scope`,t.orgId],{cwd:e.projectRoot,onOutput:createOutput,signal:e.signal})),u=parseConnectorRef(parseJson(l.stdout))?.id??CREATED_CONNECTOR.exec(c.join(`
3
3
  `))?.[1],d=l.ok?parseCreatedConnector(l.stdout,a):void 0;if(d!==void 0)return{kind:`created`,connector:d};let f=l.ok?`The ${e.service} connector does not support ${a} credentials.`:connectorCreationFailure(e.service,l.stderr);if(u!==void 0){try{await cleanupCreatedConnectionConnector({log:e.log,projectRoot:e.projectRoot,connectorId:u,orgId:t.orgId})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`${f} ${t}`)}e.signal?.throwIfAborted()}throw Error(f)}}async function setupConnectionConnector(e){let t=e.principalType??`user`,n=createPromptCommandOutput(e.log),r=e.project,i=await listConnectors(e,r,n),a=i.find(t=>connectorMatchesCanonicalName(t,e.canonicalConnectorName)),o=`Could not find a connector named ${e.canonicalConnectorName}.`;if(a!==void 0){if(await supportsPrincipalType(e,r,a,n)){if(await attach(e,r,a.uid,n))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 ${t} credentials.`}let s=await resolveFallbackConnector(e,r,n,i,o);if(!await attach(e,r,s.connector.uid,n)){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:r.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,21 +1,5 @@
1
1
  import { type ModelConnectionSelection } from "#internal/model-auth/store.js";
2
2
  import type { Prompter } from "#setup/prompter.js";
3
- export declare const CONNECTION_OPTIONS: readonly [{
4
- readonly value: "vercel";
5
- readonly label: "Vercel Account";
6
- }, {
7
- readonly value: "ai-gateway-key";
8
- readonly label: "Vercel AI Gateway API Key";
9
- }, {
10
- readonly value: "chatgpt";
11
- readonly label: "ChatGPT Subscription";
12
- }, {
13
- readonly value: "openai";
14
- readonly label: "OpenAI API Key";
15
- }, {
16
- readonly value: "anthropic";
17
- readonly label: "Anthropic API Key";
18
- }];
19
3
  type Team = {
20
4
  teamId: string;
21
5
  teamName: string;
@@ -1 +1 @@
1
- import{ensureChatGptAuth}from"./chatgpt-auth.js";import{withLoginProgress}from"./model-login-progress.js";import{loginVercelModel}from"./vercel-model-login.js";import{resolveModelApiKey}from"#internal/model-auth/transport.js";import{getVercelOidcToken}from"#compiled/@vercel/oidc/index.js";import{getDefaultCodexTokenBroker}from"#public/models/openai/chatgpt/token-broker.js";import{readVercelCliConnection,refreshVercelCliConnection}from"#internal/model-auth/vercel-cli.js";import{availableDirectModels}from"#internal/model-auth/available-models.js";import{modelKeySecretName,readVercelSession,writeModelSecret}from"#internal/model-auth/store.js";import{VERCEL_MODEL_CLIENT_ID,resolveVercelSession,validateVercelAccess}from"#internal/model-auth/vercel.js";import{validateGatewayApiKey}from"#setup/validate-gateway-key.js";const CONNECTION_OPTIONS=[{value:`vercel`,label:`Vercel Account`},{value:`ai-gateway-key`,label:`Vercel AI Gateway API Key`},{value:`chatgpt`,label:`ChatGPT Subscription`},{value:`openai`,label:`OpenAI API Key`},{value:`anthropic`,label:`Anthropic API Key`}];async function reuseModelConnection(e,t,n){switch(e){case`chatgpt`:return(await getDefaultCodexTokenBroker().refreshState()).kind===`ready`?{selected:e}:void 0;case`vercel`:{if(!await readVercelSession(VERCEL_MODEL_CLIENT_ID))return;let r=await resolveVercelSession();return t??={teamId:r.teamId,teamName:r.teamName},await validateVercelAccess(r.accessToken,t.teamId,n),{selected:e,team:t}}case`vercel-cli`:{let r=await readVercelCliConnection();if(!r)return;t??={teamId:r.teamId,teamName:r.teamId};try{await validateVercelAccess(r.token,t.teamId,n)}catch{if(n?.throwIfAborted(),await refreshVercelCliConnection(),r=await readVercelCliConnection(),!r)return;await validateVercelAccess(r.token,t.teamId,n)}return{selected:e,team:t}}case`ai-gateway-project`:return await getVercelOidcToken().catch(()=>void 0)?{selected:e}:void 0;case`ai-gateway-key`:{let t=await resolveModelApiKey(e);return(await validateGatewayApiKey(t,n)).kind===`valid`?{selected:e}:void 0}case`openai`:case`anthropic`:return{selected:e,availableModels:await availableDirectModels(e,await resolveModelApiKey(e),n)}}}async function authenticateModelConnection(e){let{selected:t,prompter:n,signal:r}=e;if(t===`vercel`)return{selected:t,team:await loginVercelModel(n,r,e.team?.teamId)};if(t===`chatgpt`)return await ensureChatGptAuth({signal:r,log:e=>n.log.info(e)}),{selected:t};if(t===`vercel-cli`||t===`ai-gateway-project`)throw Error(`Choose a connection with /login.`);let i=CONNECTION_OPTIONS.find(e=>e.value===t).label,a=(await n.password({message:i,validate:e=>e.trim()?void 0:`Enter an API key.`})).trim(),o=await withLoginProgress(n,`Checking ${i}…`,async()=>{if(t!==`ai-gateway-key`)return availableDirectModels(t,a,r);if((await validateGatewayApiKey(a,r)).kind!==`valid`)throw Error(`Could not validate the Gateway key. Check the key and your connection, then retry.`)});return r?.throwIfAborted(),await writeModelSecret(modelKeySecretName(t),a),{selected:t,availableModels:o}}export{CONNECTION_OPTIONS,authenticateModelConnection,reuseModelConnection};
1
+ import{ensureChatGptAuth}from"./chatgpt-auth.js";import{withLoginProgress}from"./model-login-progress.js";import{loginVercelModel}from"./vercel-model-login.js";import{CONNECTION_OPTIONS}from"./model-login-options.js";import{resolveModelApiKey}from"#internal/model-auth/transport.js";import{getVercelOidcToken}from"#compiled/@vercel/oidc/index.js";import{getDefaultCodexTokenBroker}from"#public/models/openai/chatgpt/token-broker.js";import{readVercelCliConnection,refreshVercelCliConnection}from"#internal/model-auth/vercel-cli.js";import{availableDirectModels}from"#internal/model-auth/available-models.js";import{modelKeySecretName,readVercelSession,writeModelSecret}from"#internal/model-auth/store.js";import{VERCEL_MODEL_CLIENT_ID,resolveVercelSession,validateVercelAccess}from"#internal/model-auth/vercel.js";import{validateGatewayApiKey}from"#setup/validate-gateway-key.js";async function reuseModelConnection(e,t,n){switch(e){case`chatgpt`:return(await getDefaultCodexTokenBroker().refreshState()).kind===`ready`?{selected:e}:void 0;case`vercel`:{if(!await readVercelSession(VERCEL_MODEL_CLIENT_ID))return;let r=await resolveVercelSession();return t??={teamId:r.teamId,teamName:r.teamName},await validateVercelAccess(r.accessToken,t.teamId,n),{selected:e,team:t}}case`vercel-cli`:{let r=await readVercelCliConnection();if(!r)return;t??={teamId:r.teamId,teamName:r.teamId};try{await validateVercelAccess(r.token,t.teamId,n)}catch{if(n?.throwIfAborted(),await refreshVercelCliConnection(),r=await readVercelCliConnection(),!r)return;await validateVercelAccess(r.token,t.teamId,n)}return{selected:e,team:t}}case`ai-gateway-project`:return await getVercelOidcToken().catch(()=>void 0)?{selected:e}:void 0;case`ai-gateway-key`:{let t=await resolveModelApiKey(e);return(await validateGatewayApiKey(t,n)).kind===`valid`?{selected:e}:void 0}case`openai`:case`anthropic`:return{selected:e,availableModels:await availableDirectModels(e,await resolveModelApiKey(e),n)}}}async function authenticateModelConnection(e){let{selected:t,prompter:n,signal:r}=e;if(t===`vercel`)return{selected:t,team:await loginVercelModel(n,r,e.team?.teamId)};if(t===`chatgpt`)return await ensureChatGptAuth({signal:r,log:e=>n.log.info(e)}),{selected:t};if(t===`vercel-cli`||t===`ai-gateway-project`)throw Error(`Choose a connection with /login.`);let i=CONNECTION_OPTIONS.find(e=>e.value===t).label,a=(await n.password({message:i,validate:e=>e.trim()?void 0:`Enter an API key.`})).trim(),o=await withLoginProgress(n,`Checking ${i}…`,async()=>{if(t!==`ai-gateway-key`)return availableDirectModels(t,a,r);if((await validateGatewayApiKey(a,r)).kind!==`valid`)throw Error(`Could not validate the Gateway key. Check the key and your connection, then retry.`)});return r?.throwIfAborted(),await writeModelSecret(modelKeySecretName(t),a),{selected:t,availableModels:o}}export{authenticateModelConnection,reuseModelConnection};
@@ -0,0 +1,59 @@
1
+ import type { ModelConnectionSelection } from "#shared/model-connection.js";
2
+ export declare const LOGIN_CONNECTION_OPTIONS: readonly [{
3
+ readonly command: "vercel";
4
+ readonly commandOrder: 0;
5
+ readonly value: "vercel";
6
+ readonly label: "Vercel Account";
7
+ }, {
8
+ readonly command: "vercel-api-key";
9
+ readonly commandOrder: 2;
10
+ readonly value: "ai-gateway-key";
11
+ readonly label: "Vercel AI Gateway API Key";
12
+ }, {
13
+ readonly command: "chatgpt";
14
+ readonly commandOrder: 1;
15
+ readonly value: "chatgpt";
16
+ readonly label: "ChatGPT Subscription";
17
+ }, {
18
+ readonly command: "openai-api-key";
19
+ readonly commandOrder: 3;
20
+ readonly value: "openai";
21
+ readonly label: "OpenAI API Key";
22
+ }, {
23
+ readonly command: "anthropic-api-key";
24
+ readonly commandOrder: 4;
25
+ readonly value: "anthropic";
26
+ readonly label: "Anthropic API Key";
27
+ }];
28
+ export declare const CONNECTION_OPTIONS: {
29
+ value: "ai-gateway-key" | "anthropic" | "chatgpt" | "openai" | "vercel";
30
+ label: "Anthropic API Key" | "ChatGPT Subscription" | "OpenAI API Key" | "Vercel AI Gateway API Key" | "Vercel Account";
31
+ }[];
32
+ export declare const LOGIN_CONNECTION_COMMAND_OPTIONS: ({
33
+ readonly command: "vercel";
34
+ readonly commandOrder: 0;
35
+ readonly value: "vercel";
36
+ readonly label: "Vercel Account";
37
+ } | {
38
+ readonly command: "vercel-api-key";
39
+ readonly commandOrder: 2;
40
+ readonly value: "ai-gateway-key";
41
+ readonly label: "Vercel AI Gateway API Key";
42
+ } | {
43
+ readonly command: "chatgpt";
44
+ readonly commandOrder: 1;
45
+ readonly value: "chatgpt";
46
+ readonly label: "ChatGPT Subscription";
47
+ } | {
48
+ readonly command: "openai-api-key";
49
+ readonly commandOrder: 3;
50
+ readonly value: "openai";
51
+ readonly label: "OpenAI API Key";
52
+ } | {
53
+ readonly command: "anthropic-api-key";
54
+ readonly commandOrder: 4;
55
+ readonly value: "anthropic";
56
+ readonly label: "Anthropic API Key";
57
+ })[];
58
+ export declare function loginConnectionForCommand(command: string): ModelConnectionSelection | undefined;
59
+ export declare const LOGIN_CONNECTION_COMMAND_HINT: string;
@@ -0,0 +1 @@
1
+ const LOGIN_CONNECTION_OPTIONS=[{command:`vercel`,commandOrder:0,value:`vercel`,label:`Vercel Account`},{command:`vercel-api-key`,commandOrder:2,value:`ai-gateway-key`,label:`Vercel AI Gateway API Key`},{command:`chatgpt`,commandOrder:1,value:`chatgpt`,label:`ChatGPT Subscription`},{command:`openai-api-key`,commandOrder:3,value:`openai`,label:`OpenAI API Key`},{command:`anthropic-api-key`,commandOrder:4,value:`anthropic`,label:`Anthropic API Key`}],CONNECTION_OPTIONS=LOGIN_CONNECTION_OPTIONS.map(({value:e,label:t})=>({value:e,label:t})),LOGIN_CONNECTION_COMMAND_OPTIONS=[...LOGIN_CONNECTION_OPTIONS].sort((e,t)=>e.commandOrder-t.commandOrder);function loginConnectionForCommand(e){return LOGIN_CONNECTION_OPTIONS.find(t=>t.command===e)?.value}const LOGIN_CONNECTION_COMMAND_HINT=LOGIN_CONNECTION_COMMAND_OPTIONS.map(e=>e.command).join(`|`);export{CONNECTION_OPTIONS,LOGIN_CONNECTION_COMMAND_HINT,LOGIN_CONNECTION_COMMAND_OPTIONS,LOGIN_CONNECTION_OPTIONS,loginConnectionForCommand};
@@ -1 +1 @@
1
- import{fetchGatewayCatalog}from"../boxes/select-model.js";import{measureLoginStage,withLoginProgress}from"./model-login-progress.js";import{CONNECTION_OPTIONS,authenticateModelConnection,reuseModelConnection}from"./model-login-connection.js";import{changeValidatedAgentModel,readAuthoredModelSelection}from"./model-source-change.js";import{MODEL_CONNECTION_ENV}from"#internal/model-auth/transport.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{inspectApplication}from"#services/inspect-application.js";import{WizardCancelledError}from"#setup/step.js";import{MODEL_HELPERS,parseModelHelper}from"#shared/model-helper.js";import{providerSettingsMatch,readProviderKeySourceSync,readProviderSelection,readProviderTeamSync,resolveAvailableProviders,writeProviderSelection}from"#setup/provider-settings.js";import{resolveModelEndpointStatus}from"#internal/resolve-model-endpoint-status.js";import{classifyModelRouting}from"#internal/classify-model-routing.js";import{availableHelperModels}from"#internal/model-auth/available-models.js";import{isModelConnection,readDefaultConnection,writeDefaultConnection}from"#internal/model-auth/store.js";function environmentConnection(e){if(e.AI_GATEWAY_API_KEY?.trim())return`ai-gateway-key`;if(e.VERCEL_OIDC_TOKEN?.trim())return`ai-gateway-project`;if(e.OPENAI_API_KEY?.trim())return`openai`;if(e.ANTHROPIC_API_KEY?.trim())return`anthropic`}async function readLoginModel(e){let t=await readAuthoredModelSelection(e);if(t!==void 0)return{selection:t,external:parseModelHelper(t)!==void 0};let n=(await inspectApplication(e)).compiledState?.manifest.config.model;return{selection:n?.routing.kind===`gateway`&&n.source===void 0?n.id:void 0,external:n?.routing.kind===`external`}}function modelSelection(e,t){let n=e===`chatgpt`||e===`openai`||e===`anthropic`?e:void 0,r=t.selection===void 0?void 0:parseModelHelper(t.selection),i=n?r?.helper===n:t.selection!==void 0&&!t.external,a=n?MODEL_HELPERS[n].defaultModel:DEFAULT_AGENT_MODEL_ID,o=r?.id??t.selection;return{helper:n,compatible:i,defaultId:a,needsModels:!i||o===a}}async function applyConnection(e,t,n,r){let{appRoot:i,prompter:a,signal:o}=e,{selected:s,team:c}=t,{helper:l,compatible:u,defaultId:d,needsModels:f}=modelSelection(s,n),p;if(f){let e=t.availableModels??r??(l?await withLoginProgress(a,`Loading available models…`,()=>availableHelperModels(l,o)):[]),i=d;if(!e.includes(i)){if(e.length===0)throw Error(`No models are available for this connection. Choose another connection with /login.`);i=await a.select({message:`Choose an available model`,search:!0,options:e.map(e=>({value:e,label:e}))})}let s=l?MODEL_HELPERS[l].prefix+i:i;(!u||s!==n.selection)&&(p=s)}let m=s===`openai`||s===`anthropic`||s===`ai-gateway-key`?e.automatic?readProviderKeySourceSync(i)??(environmentConnection(process.env)===s?`environment`:`secret`):`secret`:void 0,h={selected:s,...c,keySource:m},g=!await providerSettingsMatch(i,h),save=async()=>{if(o?.throwIfAborted(),p!==void 0){let t=await changeValidatedAgentModel({appRoot:e.agentRoot??i,slug:p});if(t.kind===`rejected`)throw Error(t.message)}g&&await writeProviderSelection(i,s,c,m),process.env[MODEL_CONNECTION_ENV]=s,m?process.env.EVE_MODEL_KEY_SOURCE=m:delete process.env.EVE_MODEL_KEY_SOURCE,c?(process.env.EVE_MODEL_TEAM=c.teamId,process.env.EVE_MODEL_TEAM_NAME=c.teamName):(delete process.env.EVE_MODEL_TEAM,delete process.env.EVE_MODEL_TEAM_NAME)};p===void 0?await save():await withLoginProgress(a,`Loading selected model…`,()=>measureLoginStage(`activation`,()=>e.withConnectionUpdate?e.withConnectionUpdate(save):save())),e.automatic||await writeDefaultConnection(s);let _=p??n.selection,v=l&&_?parseModelHelper(_)?.id:_,y;if(v){let e=l?{kind:`external`,provider:MODEL_HELPERS[l].provider}:classifyModelRouting(v);y={id:v,routing:e,endpoint:resolveModelEndpointStatus(e,{apiKey:s===`ai-gateway-key`,oidc:s===`ai-gateway-project`,account:s===`vercel`||s===`vercel-cli`,team:c?.teamName},{state:`ready`})}}return{kind:`ready`,reload:p!==void 0&&e.withConnectionUpdate===void 0,...y&&{model:y}}}async function runModelLogin(e){let{appRoot:t,prompter:n,signal:r}=e,i,getModel=()=>i??=measureLoginStage(`source_inspection`,()=>readLoginModel(e.agentRoot??t)),connect=async(i,a)=>{let o=new AbortController,s=r?AbortSignal.any([r,o.signal]):o.signal,c=getModel().then(async e=>{let t=modelSelection(i,e);return{model:e,models:!t.helper&&t.needsModels?(await measureLoginStage(`gateway_catalog`,()=>fetchGatewayCatalog(s))).filter(e=>e.type===`language`).map(e=>e.id):void 0}}).then(e=>({value:e}),e=>({error:e}));try{let r=readProviderTeamSync(t),o=await measureLoginStage(`authentication`,async()=>a?await withLoginProgress(n,`Checking model access…`,()=>reuseModelConnection(i,r,s)):await authenticateModelConnection({selected:i,team:r,prompter:n,signal:s}));if(!o)return;let l=await withLoginProgress(n,`Loading available models…`,()=>c);if(`error`in l)throw l.error;return await applyConnection({...e,automatic:a,signal:s},o,l.value.model,l.value.models)}finally{o.abort()}};try{if(e.automatic){let{model:e,selected:a}=await withLoginProgress(n,`Reading saved connection…`,async()=>{let[e,n,r,i]=await Promise.all([getModel(),readProviderSelection(t),resolveAvailableProviders(t),readDefaultConnection()]),a=e.selection===void 0?void 0:parseModelHelper(e.selection)?.helper;return{model:e,selected:n??a??environmentConnection(process.env)??(r.includes(`ai-gateway-project`)?`ai-gateway-project`:void 0)??i??`vercel-cli`}});if(e.external&&e.selection===void 0)return{kind:`ready`,reload:!1};try{let e=await connect(a,!0);if(e)return e}catch(e){i=void 0,r?.throwIfAborted(),n.log.warning(e instanceof Error?e.message:`Could not connect automatically. Retry /login.`)}}let a=e.selected;for(;;){let t=a??await n.select({message:e.connectionMessage??`Choose a connection`,search:!0,options:[...CONNECTION_OPTIONS]});if(a=void 0,!isModelConnection(t))throw Error(`Choose a model connection.`);try{let e=await connect(t,!1);if(e)return e}catch(e){if(i=void 0,e instanceof WizardCancelledError)return{kind:`cancelled`};r?.throwIfAborted(),n.log.warning(e instanceof Error?e.message:`Connection failed. Retry /login.`)}}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}export{environmentConnection,runModelLogin};
1
+ import{fetchGatewayCatalog}from"../boxes/select-model.js";import{measureLoginStage,withLoginProgress}from"./model-login-progress.js";import{CONNECTION_OPTIONS}from"./model-login-options.js";import{authenticateModelConnection,reuseModelConnection}from"./model-login-connection.js";import{changeValidatedAgentModel,readAuthoredModelSelection}from"./model-source-change.js";import{MODEL_CONNECTION_ENV}from"#internal/model-auth/transport.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{inspectApplication}from"#services/inspect-application.js";import{WizardCancelledError}from"#setup/step.js";import{MODEL_HELPERS,parseModelHelper}from"#shared/model-helper.js";import{providerSettingsMatch,readProviderKeySourceSync,readProviderSelection,readProviderTeamSync,resolveAvailableProviders,writeProviderSelection}from"#setup/provider-settings.js";import{resolveModelEndpointStatus}from"#internal/resolve-model-endpoint-status.js";import{classifyModelRouting}from"#internal/classify-model-routing.js";import{availableHelperModels}from"#internal/model-auth/available-models.js";import{isModelConnection,readDefaultConnection,writeDefaultConnection}from"#internal/model-auth/store.js";function environmentConnection(e){if(e.AI_GATEWAY_API_KEY?.trim())return`ai-gateway-key`;if(e.VERCEL_OIDC_TOKEN?.trim())return`ai-gateway-project`;if(e.OPENAI_API_KEY?.trim())return`openai`;if(e.ANTHROPIC_API_KEY?.trim())return`anthropic`}async function readLoginModel(e){let t=await readAuthoredModelSelection(e);if(t!==void 0)return{selection:t,external:parseModelHelper(t)!==void 0};let n=(await inspectApplication(e)).compiledState?.manifest.config.model;return{selection:n?.routing.kind===`gateway`&&n.source===void 0?n.id:void 0,external:n?.routing.kind===`external`}}function modelSelection(e,t){let n=e===`chatgpt`||e===`openai`||e===`anthropic`?e:void 0,r=t.selection===void 0?void 0:parseModelHelper(t.selection),i=n?r?.helper===n:t.selection!==void 0&&!t.external,a=n?MODEL_HELPERS[n].defaultModel:DEFAULT_AGENT_MODEL_ID,o=r?.id??t.selection;return{helper:n,compatible:i,defaultId:a,needsModels:!i||o===a}}async function applyConnection(e,t,n,r){let{appRoot:i,prompter:a,signal:o}=e,{selected:s,team:c}=t,{helper:l,compatible:u,defaultId:d,needsModels:f}=modelSelection(s,n),p;if(f){let e=t.availableModels??r??(l?await withLoginProgress(a,`Loading available models…`,()=>availableHelperModels(l,o)):[]),i=d;if(!e.includes(i)){if(e.length===0)throw Error(`No models are available for this connection. Choose another connection with /login.`);i=await a.select({message:`Choose an available model`,search:!0,options:e.map(e=>({value:e,label:e}))})}let s=l?MODEL_HELPERS[l].prefix+i:i;(!u||s!==n.selection)&&(p=s)}let m=s===`openai`||s===`anthropic`||s===`ai-gateway-key`?e.automatic?readProviderKeySourceSync(i)??(environmentConnection(process.env)===s?`environment`:`secret`):`secret`:void 0,h={selected:s,...c,keySource:m},g=!await providerSettingsMatch(i,h),save=async()=>{if(o?.throwIfAborted(),p!==void 0){let t=await changeValidatedAgentModel({appRoot:e.agentRoot??i,slug:p});if(t.kind===`rejected`)throw Error(t.message)}g&&await writeProviderSelection(i,s,c,m),process.env[MODEL_CONNECTION_ENV]=s,m?process.env.EVE_MODEL_KEY_SOURCE=m:delete process.env.EVE_MODEL_KEY_SOURCE,c?(process.env.EVE_MODEL_TEAM=c.teamId,process.env.EVE_MODEL_TEAM_NAME=c.teamName):(delete process.env.EVE_MODEL_TEAM,delete process.env.EVE_MODEL_TEAM_NAME)};p===void 0?await save():await withLoginProgress(a,`Loading selected model…`,()=>measureLoginStage(`activation`,()=>e.withConnectionUpdate?e.withConnectionUpdate(save):save())),e.automatic||await writeDefaultConnection(s);let _=p??n.selection,v=l&&_?parseModelHelper(_)?.id:_,y;if(v){let e=l?{kind:`external`,provider:MODEL_HELPERS[l].provider}:classifyModelRouting(v);y={id:v,routing:e,endpoint:resolveModelEndpointStatus(e,{apiKey:s===`ai-gateway-key`,oidc:s===`ai-gateway-project`,account:s===`vercel`||s===`vercel-cli`,team:c?.teamName},{state:`ready`})}}return{kind:`ready`,reload:p!==void 0&&e.withConnectionUpdate===void 0,...y&&{model:y}}}async function runModelLogin(e){let{appRoot:t,prompter:n,signal:r}=e,i,getModel=()=>i??=measureLoginStage(`source_inspection`,()=>readLoginModel(e.agentRoot??t)),connect=async(i,a)=>{let o=new AbortController,s=r?AbortSignal.any([r,o.signal]):o.signal,c=getModel().then(async e=>{let t=modelSelection(i,e);return{model:e,models:!t.helper&&t.needsModels?(await measureLoginStage(`gateway_catalog`,()=>fetchGatewayCatalog(s))).filter(e=>e.type===`language`).map(e=>e.id):void 0}}).then(e=>({value:e}),e=>({error:e}));try{let r=readProviderTeamSync(t),o=await measureLoginStage(`authentication`,async()=>a?await withLoginProgress(n,`Checking model access…`,()=>reuseModelConnection(i,r,s)):await authenticateModelConnection({selected:i,team:r,prompter:n,signal:s}));if(!o)return;let l=await withLoginProgress(n,`Loading available models…`,()=>c);if(`error`in l)throw l.error;return await applyConnection({...e,automatic:a,signal:s},o,l.value.model,l.value.models)}finally{o.abort()}};try{if(e.automatic){let{model:e,selected:a}=await withLoginProgress(n,`Reading saved connection…`,async()=>{let[e,n,r,i]=await Promise.all([getModel(),readProviderSelection(t),resolveAvailableProviders(t),readDefaultConnection()]),a=e.selection===void 0?void 0:parseModelHelper(e.selection)?.helper;return{model:e,selected:n??a??environmentConnection(process.env)??(r.includes(`ai-gateway-project`)?`ai-gateway-project`:void 0)??i??`vercel-cli`}});if(e.external&&e.selection===void 0)return{kind:`ready`,reload:!1};try{let e=await connect(a,!0);if(e)return e}catch(e){i=void 0,r?.throwIfAborted(),n.log.warning(e instanceof Error?e.message:`Could not connect automatically. Retry /login.`)}}let a=e.selected;for(;;){let t=a??await n.select({message:e.connectionMessage??`Choose a connection`,search:!0,options:[...CONNECTION_OPTIONS]});if(a=void 0,!isModelConnection(t))throw Error(`Choose a model connection.`);try{let e=await connect(t,!1);if(e)return e}catch(e){if(i=void 0,e instanceof WizardCancelledError)return{kind:`cancelled`};r?.throwIfAborted(),n.log.warning(e instanceof Error?e.message:`Connection failed. Retry /login.`)}}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}export{environmentConnection,runModelLogin};
@@ -24,8 +24,6 @@ export type ApplyModelOutcome = {
24
24
  kind: "rejected";
25
25
  message: string;
26
26
  };
27
- /** The one-line transcript form of an apply outcome (`/model <slug>`'s reply). */
28
- export declare function formatApplyModelOutcome(outcome: ApplyModelOutcome): string;
29
27
  /** Applies one completed `/model` draft through a single atomic source write. */
30
28
  export declare function changeAgentModelSettings(input: {
31
29
  readonly appRoot: string;
@@ -1 +1 @@
1
- import{readFile}from"node:fs/promises";import{join}from"node:path";import{resolveDiscoveryProject}from"#discover/project.js";import pc from"#compiled/picocolors/index.js";import{discoverAgent}from"#discover/discover-agent.js";import{parseModelHelper}from"#shared/model-helper.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{CHATGPT_MODEL_SELECTION_PREFIX}from"#shared/chatgpt-model.js";import{createCompiledRuntimeModelCatalogLoader}from"#compiler/model-catalog.js";import{readModelSelectionFromSource}from"#source-change/apply-model-selection.js";import{createStaticSourceChange}from"#source-change/static-source-change.js";function formatApplyModelSettingsOutcome(e){return e.kind===`rejected`?e.message:e.kind===`unchanged`?`Model settings are already up to date.`:e.changed.length===1&&e.changed[0]===`model`&&e.model!==void 0?`Model changed to ${pc.bold(e.model)}. Live on your next prompt.`:`Model settings updated: ${e.changed.map(t=>t===`model`?`model ${e.model??`updated`}`:t===`reasoning`?`reasoning ${e.reasoning??`provider default`}`:`Fast mode ${e.fastMode?`on`:`off`}`).join(`, `)}. Live on your next prompt.`}function formatApplyModelOutcome(e){switch(e.kind){case`changed`:return`Model changed to ${pc.bold(e.to)}. Live on your next prompt.`;case`unchanged`:return`Model is already \`${e.model}\`.`;case`rejected`:return e.message}}async function changeAgentModelSettings(e){let{appRoot:t,patch:n}=e;if(n.model.kind===`set`){let e=await validateModelSlug(t,n.model.value);if(e!==null)return{kind:`rejected`,message:e}}let r=await resolveDiscoveryProject(t),{manifest:i}=await discoverAgent(r),a=await createStaticSourceChange(i).updateModelSettings(n);if(a.kind===`bail`)return{kind:`rejected`,message:`Couldn't edit ${a.at.logicalPath}: ${a.reason}. Change the model settings by hand.`};if(a.changed.length===0)return{kind:`unchanged`};let o={kind:`changed`,changed:a.changed};return n.model.kind===`set`&&(o.model=n.model.value),n.reasoning.kind!==`keep`&&(o.reasoning=n.reasoning.kind===`set`?n.reasoning.value:null),n.gatewayServiceTier.kind!==`keep`&&(o.fastMode=n.gatewayServiceTier.kind===`set`),o}async function changeAgentModel(e){let{appRoot:t,slug:n}=e,r=await validateModelSlug(t,n);return r===null?changeValidatedAgentModel(e):{kind:`rejected`,message:r}}async function changeValidatedAgentModel(e){let{appRoot:t,slug:n}=e,r=await resolveDiscoveryProject(t),{manifest:i}=await discoverAgent(r),a=await createStaticSourceChange(i).updateModelName(n);return a.kind===`bail`?{kind:`rejected`,message:`Couldn't edit ${a.at.logicalPath}: ${a.reason}. Change \`model\` by hand.`}:a.from===a.to?{kind:`unchanged`,model:a.to}:{kind:`changed`,to:a.to}}async function validateModelSlug(e,t){if(parseModelHelper(t)!==void 0)return null;if(t.startsWith(CHATGPT_MODEL_SELECTION_PREFIX))return"Choose a bare OpenAI model id after `chatgpt/`.";if(!t.includes(`/`))return`\`${t}\` isn't a provider/model id (e.g. anthropic/claude-sonnet-5).`;let n=createCompiledRuntimeModelCatalogLoader(e);try{if(await n.getModelLimits(formatLanguageModelGatewayId(t))===null)return`I couldn't confirm \`${t}\` in the AI Gateway model catalog, so I didn't change agent.ts.`}catch{return null}return null}async function readAuthoredModelSelection(e){let t=await resolveDiscoveryProject(e),{manifest:n}=await discoverAgent(t);if(!n.configModule)return;let r=await readFile(join(t.agentRoot,n.configModule.logicalPath),`utf8`);return readModelSelectionFromSource(r)}export{changeAgentModel,changeAgentModelSettings,changeValidatedAgentModel,formatApplyModelOutcome,formatApplyModelSettingsOutcome,readAuthoredModelSelection,validateModelSlug};
1
+ import{readFile}from"node:fs/promises";import{join}from"node:path";import{resolveDiscoveryProject}from"#discover/project.js";import pc from"#compiled/picocolors/index.js";import{discoverAgent}from"#discover/discover-agent.js";import{parseModelHelper}from"#shared/model-helper.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{CHATGPT_MODEL_SELECTION_PREFIX}from"#shared/chatgpt-model.js";import{createCompiledRuntimeModelCatalogLoader}from"#compiler/model-catalog.js";import{readModelSelectionFromSource}from"#source-change/apply-model-selection.js";import{createStaticSourceChange}from"#source-change/static-source-change.js";function formatApplyModelSettingsOutcome(e){return e.kind===`rejected`?e.message:e.kind===`unchanged`?`Model settings are already up to date.`:e.changed.length===1&&e.changed[0]===`model`&&e.model!==void 0?`Model changed to ${pc.bold(e.model)}. Live on your next prompt.`:`Model settings updated: ${e.changed.map(t=>t===`model`?`model ${e.model??`updated`}`:t===`reasoning`?`reasoning ${e.reasoning??`provider default`}`:`Fast mode ${e.fastMode?`on`:`off`}`).join(`, `)}. Live on your next prompt.`}async function changeAgentModelSettings(e){let{appRoot:t,patch:n}=e;if(n.model.kind===`set`){let e=await validateModelSlug(t,n.model.value);if(e!==null)return{kind:`rejected`,message:e}}let r=await resolveDiscoveryProject(t),{manifest:i}=await discoverAgent(r),a=await createStaticSourceChange(i).updateModelSettings(n);if(a.kind===`bail`)return{kind:`rejected`,message:`Couldn't edit ${a.at.logicalPath}: ${a.reason}. Change the model settings by hand.`};if(a.changed.length===0)return{kind:`unchanged`};let o={kind:`changed`,changed:a.changed};return n.model.kind===`set`&&(o.model=n.model.value),n.reasoning.kind!==`keep`&&(o.reasoning=n.reasoning.kind===`set`?n.reasoning.value:null),n.gatewayServiceTier.kind!==`keep`&&(o.fastMode=n.gatewayServiceTier.kind===`set`),o}async function changeAgentModel(e){let{appRoot:t,slug:n}=e,r=await validateModelSlug(t,n);return r===null?changeValidatedAgentModel(e):{kind:`rejected`,message:r}}async function changeValidatedAgentModel(e){let{appRoot:t,slug:n}=e,r=await resolveDiscoveryProject(t),{manifest:i}=await discoverAgent(r),a=await createStaticSourceChange(i).updateModelName(n);return a.kind===`bail`?{kind:`rejected`,message:`Couldn't edit ${a.at.logicalPath}: ${a.reason}. Change \`model\` by hand.`}:a.from===a.to?{kind:`unchanged`,model:a.to}:{kind:`changed`,to:a.to}}async function validateModelSlug(e,t){if(parseModelHelper(t)!==void 0)return null;if(t.startsWith(CHATGPT_MODEL_SELECTION_PREFIX))return"Choose a bare OpenAI model id after `chatgpt/`.";if(!t.includes(`/`))return`\`${t}\` isn't a provider/model id (e.g. anthropic/claude-sonnet-5).`;let n=createCompiledRuntimeModelCatalogLoader(e);try{if(await n.getModelLimits(formatLanguageModelGatewayId(t))===null)return`I couldn't confirm \`${t}\` in the AI Gateway model catalog, so I didn't change agent.ts.`}catch{return null}return null}async function readAuthoredModelSelection(e){let t=await resolveDiscoveryProject(e),{manifest:n}=await discoverAgent(t);if(!n.configModule)return;let r=await readFile(join(t.agentRoot,n.configModule.logicalPath),`utf8`);return readModelSelectionFromSource(r)}export{changeAgentModel,changeAgentModelSettings,changeValidatedAgentModel,formatApplyModelSettingsOutcome,readAuthoredModelSelection,validateModelSlug};
@@ -6,37 +6,39 @@ export interface RegistrySessionDeps {
6
6
  detectDeployment: typeof detectDeployment;
7
7
  runDeployFlow: typeof runDeployFlow;
8
8
  }
9
- export interface RegistrySessionItemResult {
9
+ export type RegistrySessionOutcome = {
10
+ kind: "installed";
10
11
  title: string;
11
12
  facts: readonly RegistrySetupFact[];
12
13
  output: readonly string[];
13
14
  }
14
- /** An item the user chose to skip after its installation could not complete. */
15
- export interface RegistrySessionItemFailure {
15
+ /** Files were added, but the item's setup was cancelled or skipped. */
16
+ | {
17
+ kind: "incomplete";
16
18
  title: string;
17
- /** User-facing installation error, including any actionable follow-up lines. */
18
- message: string;
19
+ resumeCommand: string;
19
20
  }
20
- export type RegistrySessionOutcome = ({
21
- kind: "installed";
22
- } & RegistrySessionItemResult) | ({
21
+ /** User-facing installation error, including any actionable follow-up lines. */
22
+ | {
23
23
  kind: "failed";
24
- } & RegistrySessionItemFailure) | {
24
+ title: string;
25
+ message: string;
26
+ }
27
+ /** Stopped before anything was written. */
28
+ | {
25
29
  kind: "cancelled";
26
30
  title: string;
27
31
  };
28
32
  export interface RegistrySessionResult {
29
- items: readonly RegistrySessionItemResult[];
30
- /** Installation failures retained when a user skips an item. */
31
- failures: readonly RegistrySessionItemFailure[];
32
33
  /** Every item outcome in installation order. */
33
- outcomes?: readonly RegistrySessionOutcome[];
34
+ outcomes: readonly RegistrySessionOutcome[];
34
35
  /** Setup stopped outside an individual item after preserving settled results. */
35
36
  cancelled?: true;
36
37
  deployed?: "production";
37
38
  }
38
39
  export interface RegistrySession {
39
40
  add(title: string, output: readonly string[], setup?: RegistrySetupCompletion): void;
41
+ addIncomplete(title: string, resumeCommand: string): void;
40
42
  addFailure(title: string, message: string): void;
41
43
  addCancellation(title: string): void;
42
44
  result(deployed?: "production"): RegistrySessionResult;
@@ -1 +1 @@
1
- import"./deploy.js";import"#setup/project-resolution.js";function createRegistrySession(e){let t=[],n=!1;function result(e){let n={items:t.flatMap(e=>e.kind===`installed`?[{title:e.title,facts:e.facts,output:e.output}]:[]),failures:t.flatMap(e=>e.kind===`failed`?[{title:e.title,message:e.message}]:[])};return t.some(e=>e.kind!==`installed`)&&(n.outcomes=t),e!==void 0&&(n.deployed=e),n}return{add(e,r,i={facts:[]}){t.push({kind:`installed`,title:e,facts:i.facts,output:r}),n||=i.deploymentRequired===!0},addFailure(e,n){t.push({kind:`failed`,title:e,message:n})},addCancellation(e){t.push({kind:`cancelled`,title:e})},result,async continueAfterInstall(t){if(!n)return result();let r=await e.detectDeployment(t.appRoot,{signal:t.signal}),i=r.state===`linked`||r.state===`deployed`;for(t.prompter.replaceContent?.();;){if(await t.prompter.select({message:`What would you like to do next?`,initialValue:`finish`,hintLayout:`inline`,options:[...i?[{value:`deploy`,label:`Deploy`}]:[],{value:`finish`,label:`Start chatting`}]})===`finish`)return result();if(await t.prompter.select({message:`Deploy to prod?`,initialValue:`yes`,options:[{value:`yes`,label:`Yes`},{value:`back`,label:`Back`}]})!==`back`)return result((await e.runDeployFlow({appRoot:t.appRoot,prompter:t.prompter,signal:t.signal,interactive:!0})).kind===`deployed`?`production`:void 0)}}}}export{createRegistrySession};
1
+ import"./deploy.js";import"#setup/project-resolution.js";function createRegistrySession(e){let t=[],n=!1;function result(e){let n={outcomes:[...t]};return e!==void 0&&(n.deployed=e),n}return{add(e,r,i={facts:[]}){t.push({kind:`installed`,title:e,facts:i.facts,output:r}),n||=i.deploymentRequired===!0},addIncomplete(e,n){t.push({kind:`incomplete`,title:e,resumeCommand:n})},addFailure(e,n){t.push({kind:`failed`,title:e,message:n})},addCancellation(e){t.push({kind:`cancelled`,title:e})},result,async continueAfterInstall(t){if(!n)return result();let r=await e.detectDeployment(t.appRoot,{signal:t.signal}),i=r.state===`linked`||r.state===`deployed`;for(t.prompter.replaceContent?.();;){if(await t.prompter.select({message:`What would you like to do next?`,initialValue:`finish`,hintLayout:`inline`,options:[...i?[{value:`deploy`,label:`Deploy`}]:[],{value:`finish`,label:`Start chatting`}]})===`finish`)return result();if(await t.prompter.select({message:`Deploy to prod?`,initialValue:`yes`,options:[{value:`yes`,label:`Yes`},{value:`back`,label:`Back`}]})!==`back`)return result((await e.runDeployFlow({appRoot:t.appRoot,prompter:t.prompter,signal:t.signal,interactive:!0})).kind===`deployed`?`production`:void 0)}}}}export{createRegistrySession};
@@ -1 +1 @@
1
- import{createRegistrySession}from"./registry-session.js";import{WizardCancelledError}from"#setup/step.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{withSpinner}from"#setup/with-spinner.js";function label(e){return e.name}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 hasSettledOutcomes(e){return e!==void 0&&(e.items.length>0||e.failures.length>0||e.outcomes!==void 0)}async function runRegistryFlow(e){let t;try{let n=e.initialAddress?.trim(),r;if(n!==void 0&&n!==``)r=[{address:n,name:n,source:`Registry`}];else{let t=e.deps?.browseRegistryCatalog??(await import(`#cli/commands/registry.js`)).browseRegistryCatalog,n=await withSpinner(e.prompter,`Loading registry…`,()=>t(e.appRoot)),i=[...n.items],a=await e.prompter.select({message:`Add to your agent`,search:!0,options:i.map(e=>({value:e.address,label:e.name,hint:e.address,keywords:[e.title??``,e.description??``]})),notices:n.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))}),o=i.find(e=>e.address===a);if(o===void 0)throw Error(`That registry item is no longer available. Run /add to try again.`);r=[o]}let i=e.deps?.installRegistryItem??(await import(`#cli/commands/registry.js`)).installRegistryItem,a=e.installRoot??e.appRoot,o=e.deps?.detectDeployment??(await import(`#setup/project-resolution.js`)).detectDeployment,s=e.deps?.runDeployFlow??(await import(`./deploy.js`)).runDeployFlow;t=createRegistrySession({detectDeployment:o,runDeployFlow:s}),e.onScreen?.({screen:`registry_install`});let c=t;for(let[t,n]of r.entries()){e.signal?.throwIfAborted(),e.onItemStart?.(n,t,r.length);try{let install=(t=e.signal)=>i(a,n.address,{silent:!0,prompter:e.prompter,signal:t}),run=()=>e.runItem===void 0?install():e.runItem(install),t=await(e.prompter.withExclusiveTerminal?.(run)??run());c.add(label(n),t.output,t.setup)}catch(t){if(e.signal?.throwIfAborted(),t instanceof WizardCancelledError){c.addCancellation(label(n));continue}if(t instanceof HumanActionRequiredError)throw t;let r=(t instanceof Error?t.message:String(t)).trim()||`Installation failed.`;c.addFailure(label(n),r)}}return{kind:`done`,result:await c.continueAfterInstall({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal})}}catch(e){if(e instanceof WizardCancelledError){let e=t?.result();return hasSettledOutcomes(e)?{kind:`done`,result:{...e,cancelled:!0}}:{kind:`cancelled`}}let n=t?.result();throw hasSettledOutcomes(n)?new RegistryFlowFailedError(e,n):e}}export{RegistryFlowFailedError,runRegistryFlow};
1
+ import{createRegistrySession}from"./registry-session.js";import{WizardCancelledError}from"#setup/step.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{withSpinner}from"#setup/with-spinner.js";function label(e){return e.name}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 hasSettledOutcomes(e){return e!==void 0&&e.outcomes.length>0}async function runRegistryFlow(e){let t;try{let n=e.initialAddress?.trim(),r;if(n!==void 0&&n!==``)r=[{address:n,name:n,source:`Registry`}];else{let t=e.deps?.browseRegistryCatalog??(await import(`#cli/commands/registry.js`)).browseRegistryCatalog,n=await withSpinner(e.prompter,`Loading registry…`,()=>t(e.appRoot)),i=[...n.items],a=await e.prompter.select({message:`Add to your agent`,search:!0,options:i.map(e=>({value:e.address,label:e.name,hint:e.address,keywords:[e.title??``,e.description??``]})),notices:n.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))}),o=i.find(e=>e.address===a);if(o===void 0)throw Error(`That registry item is no longer available. Run /add to try again.`);r=[o]}let i=e.deps?.installRegistryItem??(await import(`#cli/commands/registry.js`)).installRegistryItem,a=e.installRoot??e.appRoot,o=e.deps?.detectDeployment??(await import(`#setup/project-resolution.js`)).detectDeployment,s=e.deps?.runDeployFlow??(await import(`./deploy.js`)).runDeployFlow;t=createRegistrySession({detectDeployment:o,runDeployFlow:s}),e.onScreen?.({screen:`registry_install`});let c=t;for(let[t,n]of r.entries()){e.signal?.throwIfAborted(),e.onItemStart?.(n,t,r.length);try{let install=(t=e.signal)=>i(a,n.address,{silent:!0,prompter:e.prompter,signal:t}),run=()=>e.runItem===void 0?install():e.runItem(install),t=await(e.prompter.withExclusiveTerminal?.(run)??run());t.setupIncomplete===void 0?c.add(label(n),t.output,t.setup):c.addIncomplete(label(n),t.setupIncomplete.resumeCommand)}catch(t){if(e.signal?.throwIfAborted(),t instanceof WizardCancelledError){c.addCancellation(label(n));continue}if(t instanceof HumanActionRequiredError)throw t;let r=(t instanceof Error?t.message:String(t)).trim()||`Installation failed.`;c.addFailure(label(n),r)}}return{kind:`done`,result:await c.continueAfterInstall({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal})}}catch(e){if(e instanceof WizardCancelledError){let e=t?.result();return hasSettledOutcomes(e)?{kind:`done`,result:{...e,cancelled:!0}}:{kind:`cancelled`}}let n=t?.result();throw hasSettledOutcomes(n)?new RegistryFlowFailedError(e,n):e}}export{RegistryFlowFailedError,runRegistryFlow};
@@ -1 +1 @@
1
- import{withLoginProgress}from"./model-login-progress.js";import{isObject}from"#shared/guards.js";import{rememberModelTeamSlug}from"#internal/model-auth/vercel-team.js";import{setTimeout}from"node:timers/promises";import{readVercelCliTeam}from"#internal/model-auth/vercel-cli.js";import{openUrl}from"#setup/primitives/open-url.js";import{writeVercelSession}from"#internal/model-auth/store.js";import{VERCEL_MODEL_CLIENT_ID,VERCEL_OAUTH_ISSUER,authJson,sessionFromToken,validateVercelAccess,vercelApiJson,vercelOAuthEndpoints}from"#internal/model-auth/vercel.js";async function loginVercelModel(e,t,n){let{endpoints:r,device:i}=await withLoginProgress(e,`Opening Vercel sign-in…`,async()=>{let e=await vercelOAuthEndpoints(t);return{endpoints:e,device:await authJson(e.device,{method:`POST`,body:new URLSearchParams({client_id:VERCEL_MODEL_CLIENT_ID,scope:`openid offline_access`}),signal:t})}});if(typeof i.device_code!=`string`||typeof i.verification_uri!=`string`||typeof i.user_code!=`string`)throw Error(`Could not start Vercel sign-in. Retry /login.`);let a=typeof i.verification_uri_complete==`string`?i.verification_uri_complete:i.verification_uri;if(URL.parse(a)?.origin!==VERCEL_OAUTH_ISSUER)throw Error(`Vercel returned an unexpected sign-in URL. Retry /login.`);e.log.info(`Open ${a} · code ${i.user_code}`),openUrl(a);let o=typeof i.interval==`number`?Math.max(1,Math.min(30,i.interval)):5,s=typeof i.expires_in==`number`?Math.min(600,i.expires_in):300,c=Date.now()+s*1e3,l=e.log.spinner?.(`Finish signing in to Vercel in your browser`,{kind:`external-action`,emphasis:`your browser`}),u;try{for(;Date.now()<c;){await setTimeout(o*1e3,void 0,{signal:t});let e=await authJson(r.token,{method:`POST`,body:new URLSearchParams({client_id:VERCEL_MODEL_CLIENT_ID,grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:i.device_code}),signal:t});if(e.error!==`authorization_pending`){if(e.error===`slow_down`){o=Math.min(30,o+5);continue}if(e.error)throw Error(`Vercel sign-in was declined or expired. Retry /login.`);u=sessionFromToken(e);break}}}finally{l?.stop()}if(!u)throw Error(`Vercel sign-in timed out. Retry /login.`);let[d,f]=await withLoginProgress(e,`Loading Vercel teams…`,()=>Promise.all([loadTeams(u.accessToken,t),readVercelCliTeam()])),p=d.find(e=>e.id===n)??d.find(e=>e.id===f),m=d.length===1?d[0]:void 0;for(;;){if(!m){let t=await e.select({message:`Choose a Vercel team`,search:!0,initialValue:p?.id,options:d.map(e=>({value:e.id,label:e.name}))});m=d.find(e=>e.id===t)}if(!m)throw Error(`That Vercel team is no longer available. Retry /login.`);let n=m;try{await withLoginProgress(e,`Checking AI Gateway access for ${m.slug??m.name}…`,()=>validateVercelAccess(u.accessToken,n.id,t));break}catch(n){if(t?.throwIfAborted(),d.length===1)throw n;e.log.warning(`AI Gateway is unavailable for that team. Choose another team or press Esc to return.`),m=void 0}}return u.teamId=m.id,u.teamName=m.name,t?.throwIfAborted(),await writeVercelSession(u,VERCEL_MODEL_CLIENT_ID),m.slug&&rememberModelTeamSlug(m.id,m.slug),{teamId:m.id,teamName:m.name}}async function loadTeams(e,t){let n=[],r;for(let i=0;i<20;i++){let i=await vercelApiJson(`https://api.vercel.com/v2/teams?limit=100${r===void 0?``:`&until=${r}`}`,{headers:{authorization:`Bearer ${e}`},signal:t});if(!Array.isArray(i.teams))throw Error(`Could not list Vercel teams. Retry /login.`);for(let e of i.teams)isObject(e)&&typeof e.id==`string`&&typeof e.name==`string`&&n.push({id:e.id,name:e.name,slug:typeof e.slug==`string`?e.slug:void 0});if(r=isObject(i.pagination)&&typeof i.pagination.next==`number`?i.pagination.next:void 0,r===void 0)break}if(n.length===0)throw Error(`No Vercel team is available. Join a team or choose another connection in /login.`);return n}export{loginVercelModel};
1
+ import{withLoginProgress}from"./model-login-progress.js";import{isObject}from"#shared/guards.js";import{rememberModelTeamSlug}from"#internal/model-auth/vercel-team.js";import{setTimeout}from"node:timers/promises";import{readVercelCliTeam}from"#internal/model-auth/vercel-cli.js";import{openUrl}from"#setup/primitives/open-url.js";import{writeVercelSession}from"#internal/model-auth/store.js";import{VERCEL_MODEL_CLIENT_ID,VERCEL_OAUTH_ISSUER,authJson,sessionFromToken,validateVercelAccess,vercelApiJson,vercelOAuthEndpoints}from"#internal/model-auth/vercel.js";async function loginVercelModel(e,t,n){let{endpoints:r,device:i}=await withLoginProgress(e,`Opening Vercel sign-in…`,async()=>{let e=await vercelOAuthEndpoints(t);return{endpoints:e,device:await authJson(e.device,{method:`POST`,body:new URLSearchParams({client_id:VERCEL_MODEL_CLIENT_ID,scope:`openid offline_access`}),signal:t})}});if(typeof i.device_code!=`string`||typeof i.verification_uri!=`string`||typeof i.user_code!=`string`)throw Error(`Could not start Vercel sign-in. Retry /login.`);let a=typeof i.verification_uri_complete==`string`?i.verification_uri_complete:i.verification_uri;if(URL.parse(a)?.origin!==VERCEL_OAUTH_ISSUER)throw Error(`Vercel returned an unexpected sign-in URL. Retry /login.`);e.log.info(`Open ${a} · code ${i.user_code}`),openUrl(a);let o=typeof i.interval==`number`?Math.max(1,Math.min(30,i.interval)):5,s=typeof i.expires_in==`number`?Math.min(600,i.expires_in):300,c=Date.now()+s*1e3,l=e.log.spinner?.(`Finish signing in to Vercel in your browser`),u;try{for(;Date.now()<c;){await setTimeout(o*1e3,void 0,{signal:t});let e=await authJson(r.token,{method:`POST`,body:new URLSearchParams({client_id:VERCEL_MODEL_CLIENT_ID,grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:i.device_code}),signal:t});if(e.error!==`authorization_pending`){if(e.error===`slow_down`){o=Math.min(30,o+5);continue}if(e.error)throw Error(`Vercel sign-in was declined or expired. Retry /login.`);u=sessionFromToken(e);break}}}finally{l?.stop()}if(!u)throw Error(`Vercel sign-in timed out. Retry /login.`);let[d,f]=await withLoginProgress(e,`Loading Vercel teams…`,()=>Promise.all([loadTeams(u.accessToken,t),readVercelCliTeam()])),p=d.find(e=>e.id===n)??d.find(e=>e.id===f),m=d.length===1?d[0]:void 0;for(;;){if(!m){let t=await e.select({message:`Choose a Vercel team`,search:!0,initialValue:p?.id,options:d.map(e=>({value:e.id,label:e.name}))});m=d.find(e=>e.id===t)}if(!m)throw Error(`That Vercel team is no longer available. Retry /login.`);let n=m;try{await withLoginProgress(e,`Checking AI Gateway access for ${m.slug??m.name}…`,()=>validateVercelAccess(u.accessToken,n.id,t));break}catch(n){if(t?.throwIfAborted(),d.length===1)throw n;e.log.warning(`AI Gateway is unavailable for that team. Choose another team or press Esc to return.`),m=void 0}}return u.teamId=m.id,u.teamName=m.name,t?.throwIfAborted(),await writeVercelSession(u,VERCEL_MODEL_CLIENT_ID),m.slug&&rememberModelTeamSlug(m.id,m.slug),{teamId:m.id,teamName:m.name}}async function loadTeams(e,t){let n=[],r;for(let i=0;i<20;i++){let i=await vercelApiJson(`https://api.vercel.com/v2/teams?limit=100${r===void 0?``:`&until=${r}`}`,{headers:{authorization:`Bearer ${e}`},signal:t});if(!Array.isArray(i.teams))throw Error(`Could not list Vercel teams. Retry /login.`);for(let e of i.teams)isObject(e)&&typeof e.id==`string`&&typeof e.name==`string`&&n.push({id:e.id,name:e.name,slug:typeof e.slug==`string`?e.slug:void 0});if(r=isObject(i.pagination)&&typeof i.pagination.next==`number`?i.pagination.next:void 0,r===void 0)break}if(n.length===0)throw Error(`No Vercel team is available. Join a team or choose another connection in /login.`);return n}export{loginVercelModel};