diff --git a/setup/channels/teams.ts b/setup/channels/teams.ts index 3691beb3b..9375995a7 100644 --- a/setup/channels/teams.ts +++ b/setup/channels/teams.ts @@ -95,12 +95,25 @@ export async function runTeamsChannel(_displayName: string): Promise { +}): Promise<'continue' | 'back'> { note( [ `1. In ${AZURE_PORTAL_URL}, search "App registrations" → "New registration"`, @@ -262,15 +275,17 @@ async function stepAppRegistration(args: { ); } - await stepGate({ + const gate = await stepGate({ stepName: 'teams-app-registration', stepDescription: 'registering an app in Azure and collecting App ID + tenant type', reshow: () => stepAppRegistration(args), args, }); + if (gate === 'back') return 'back'; args.completed.push( `App registered: ${args.collected.appId} (${args.collected.appType})`, ); + return 'continue'; } async function askAppType(args: { @@ -313,7 +328,7 @@ async function askAppType(args: { async function stepClientSecret(args: { collected: Collected; completed: string[]; -}): Promise { +}): Promise<'continue' | 'back'> { note( [ `1. In your app registration, open "Certificates & secrets"`, @@ -356,13 +371,15 @@ async function stepClientSecret(args: { break; } - await stepGate({ + const gate = await stepGate({ stepName: 'teams-client-secret', stepDescription: 'creating and copying the client secret', reshow: () => stepClientSecret(args), args, }); + if (gate === 'back') return 'back'; args.completed.push('Client secret captured.'); + return 'continue'; } // ─── step: Azure Bot resource ────────────────────────────────────────── @@ -370,7 +387,7 @@ async function stepClientSecret(args: { async function stepAzureBot(args: { collected: Collected; completed: string[]; -}): Promise { +}): Promise<'continue' | 'back'> { const endpoint = `${args.collected.publicUrl}/api/webhooks/teams`; const tenantFlag = args.collected.appType === 'SingleTenant' @@ -405,14 +422,16 @@ async function stepAzureBot(args: { 'Step 3 of 6 — Create Azure Bot resource', ); - await stepGate({ + const gate = await stepGate({ stepName: 'teams-azure-bot', stepDescription: 'creating an Azure Bot resource linked to the app registration and setting the messaging endpoint', reshow: () => stepAzureBot(args), args, }); + if (gate === 'back') return 'back'; args.completed.push('Azure Bot created; messaging endpoint configured.'); + return 'continue'; } // ─── step: enable Teams channel ──────────────────────────────────────── @@ -420,7 +439,7 @@ async function stepAzureBot(args: { async function stepEnableTeamsChannel(args: { collected: Collected; completed: string[]; -}): Promise { +}): Promise<'continue' | 'back'> { note( [ '1. Open your Azure Bot resource → Channels', @@ -431,13 +450,15 @@ async function stepEnableTeamsChannel(args: { ].join('\n'), 'Step 4 of 6 — Enable Teams channel on the bot', ); - await stepGate({ + const gate = await stepGate({ stepName: 'teams-enable-channel', stepDescription: 'enabling the Microsoft Teams channel on the Azure Bot resource', reshow: () => stepEnableTeamsChannel(args), args, }); + if (gate === 'back') return 'back'; args.completed.push('Teams channel enabled on the bot.'); + return 'continue'; } // ─── step: manifest zip ──────────────────────────────────────────────── @@ -490,7 +511,7 @@ async function stepSideload(args: { collected: Collected; completed: string[]; zipPath: string; -}): Promise { +}): Promise<'continue' | 'back'> { note( [ '1. Open Microsoft Teams', @@ -505,13 +526,15 @@ async function stepSideload(args: { ].join('\n'), 'Step 5 of 6 — Sideload the app into Teams', ); - await stepGate({ + const gate = await stepGate({ stepName: 'teams-sideload', stepDescription: 'uploading the generated zip into Teams as a custom app', - reshow: () => stepSideload(args), + reshow: () => stepSideload({ ...args, zipPath: args.zipPath }), args, }); + if (gate === 'back') return 'back'; args.completed.push('App sideloaded into Teams.'); + return 'continue'; } // ─── step: install adapter ───────────────────────────────────────────── @@ -623,9 +646,9 @@ async function finishWithHandoff( async function stepGate(args: { stepName: string; stepDescription: string; - reshow: () => Promise | Promise; + reshow: () => Promise<'continue' | 'back'>; args: { collected: Collected; completed: string[] }; -}): Promise { +}): Promise<'continue' | 'back'> { while (true) { const choice = ensureAnswer( await brightSelect({ @@ -634,10 +657,12 @@ async function stepGate(args: { { value: 'done', label: "Done — let's continue" }, { value: 'help', label: 'Stuck — hand me off to Claude' }, { value: 'reshow', label: 'Show me the steps again' }, + { value: 'back', label: '← Back to channel selection' }, ], }), ); - if (choice === 'done') return; + if (choice === 'done') return 'continue'; + if (choice === 'back') return 'back'; if (choice === 'help') { await offerHandoff({ step: args.stepName, @@ -647,8 +672,7 @@ async function stepGate(args: { continue; } if (choice === 'reshow') { - await args.reshow(); - return; + return args.reshow(); } } }