Merge pull request #2313 from alipgoldberg/setup/teams-step-gate-back

setup: add back-to-channels exit at every Teams step gate
This commit is contained in:
Daniel M
2026-05-07 11:16:26 +03:00
committed by GitHub
+45 -21
View File
@@ -95,12 +95,25 @@ export async function runTeamsChannel(_displayName: string): Promise<ChannelFlow
const prereqsResult = await confirmPrereqs({ collected, completed });
if (prereqsResult === 'back') return BACK_TO_CHANNEL_SELECTION;
await stepPublicUrl({ collected, completed });
await stepAppRegistration({ collected, completed });
await stepClientSecret({ collected, completed });
await stepAzureBot({ collected, completed });
await stepEnableTeamsChannel({ collected, completed });
if (await stepAppRegistration({ collected, completed }) === 'back') {
return BACK_TO_CHANNEL_SELECTION;
}
if (await stepClientSecret({ collected, completed }) === 'back') {
return BACK_TO_CHANNEL_SELECTION;
}
if (await stepAzureBot({ collected, completed }) === 'back') {
return BACK_TO_CHANNEL_SELECTION;
}
if (await stepEnableTeamsChannel({ collected, completed }) === 'back') {
return BACK_TO_CHANNEL_SELECTION;
}
const manifestResult = await stepGenerateManifest({ collected, completed });
await stepSideload({ collected, completed, zipPath: manifestResult.zipPath });
if (
await stepSideload({ collected, completed, zipPath: manifestResult.zipPath })
=== 'back'
) {
return BACK_TO_CHANNEL_SELECTION;
}
await installAdapter(collected);
completed.push('Adapter installed and service restarted.');
@@ -229,7 +242,7 @@ async function stepPublicUrl(args: { collected: Collected; completed: string[] }
async function stepAppRegistration(args: {
collected: Collected;
completed: string[];
}): Promise<void> {
}): 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<void> {
}): 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<void> {
}): 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<void> {
}): 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<void> {
}): 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<void> | Promise<unknown>;
reshow: () => Promise<'continue' | 'back'>;
args: { collected: Collected; completed: string[] };
}): Promise<void> {
}): 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();
}
}
}