setup: add ← Back option to Teams channel flow
Stacked on #2269 (back-nav scaffolding) plus the Telegram and Slack PRs. They share the same scaffolding file from #2269 — they don't compile without it, so they have to stack. Both Teams paths already had a brightSelect at the right place, so we just extend each with a Back option — no new prompts: - Existing-credentials path: Yes/No confirm becomes Yes/No/Back - Fresh-setup path: the very first stepGate ("How did that go?") gets a 4th option. Subsequent stepGates keep the original 3 options so we never lose mid-flow state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -462,7 +462,7 @@ async function main(): Promise<void> {
|
|||||||
} else if (channelChoice === 'signal') {
|
} else if (channelChoice === 'signal') {
|
||||||
await runSignalChannel(displayName!);
|
await runSignalChannel(displayName!);
|
||||||
} else if (channelChoice === 'teams') {
|
} else if (channelChoice === 'teams') {
|
||||||
await runTeamsChannel(displayName!);
|
result = await runTeamsChannel(displayName!);
|
||||||
} else if (channelChoice === 'slack') {
|
} else if (channelChoice === 'slack') {
|
||||||
result = await runSlackChannel(displayName!);
|
result = await runSlackChannel(displayName!);
|
||||||
} else if (channelChoice === 'imessage') {
|
} else if (channelChoice === 'imessage') {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import path from 'path';
|
|||||||
import * as p from '@clack/prompts';
|
import * as p from '@clack/prompts';
|
||||||
import k from 'kleur';
|
import k from 'kleur';
|
||||||
|
|
||||||
|
import { BACK_TO_CHANNEL_SELECTION, type ChannelFlowResult } from '../lib/back-nav.js';
|
||||||
import { brightSelect } from '../lib/bright-select.js';
|
import { brightSelect } from '../lib/bright-select.js';
|
||||||
import { confirmThenOpen } from '../lib/browser.js';
|
import { confirmThenOpen } from '../lib/browser.js';
|
||||||
import {
|
import {
|
||||||
@@ -57,18 +58,24 @@ interface Collected {
|
|||||||
agentName?: string;
|
agentName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runTeamsChannel(_displayName: string): Promise<void> {
|
export async function runTeamsChannel(_displayName: string): Promise<ChannelFlowResult> {
|
||||||
const collected: Collected = {};
|
const collected: Collected = {};
|
||||||
const completed: string[] = [];
|
const completed: string[] = [];
|
||||||
|
|
||||||
const existingAppId = readEnvKey('TEAMS_APP_ID');
|
const existingAppId = readEnvKey('TEAMS_APP_ID');
|
||||||
const existingPassword = readEnvKey('TEAMS_APP_PASSWORD');
|
const existingPassword = readEnvKey('TEAMS_APP_PASSWORD');
|
||||||
if (existingAppId && existingPassword) {
|
if (existingAppId && existingPassword) {
|
||||||
const reuse = ensureAnswer(await p.confirm({
|
const choice = ensureAnswer(await brightSelect<'yes' | 'no' | 'back'>({
|
||||||
message: `Found existing Teams credentials (App ID: ${existingAppId.slice(0, 8)}…). Use them?`,
|
message: `Found existing Teams credentials (App ID: ${existingAppId.slice(0, 8)}…). Use them?`,
|
||||||
initialValue: true,
|
options: [
|
||||||
|
{ value: 'yes', label: 'Yes, use the existing credentials' },
|
||||||
|
{ value: 'no', label: "No, set up new ones" },
|
||||||
|
{ value: 'back', label: '← Back to channel selection' },
|
||||||
|
],
|
||||||
|
initialValue: 'yes',
|
||||||
}));
|
}));
|
||||||
if (reuse) {
|
if (choice === 'back') return BACK_TO_CHANNEL_SELECTION;
|
||||||
|
if (choice === 'yes') {
|
||||||
collected.appId = existingAppId;
|
collected.appId = existingAppId;
|
||||||
collected.appPassword = existingPassword;
|
collected.appPassword = existingPassword;
|
||||||
collected.appType = (readEnvKey('TEAMS_APP_TYPE') as 'SingleTenant' | 'MultiTenant') || 'MultiTenant';
|
collected.appType = (readEnvKey('TEAMS_APP_TYPE') as 'SingleTenant' | 'MultiTenant') || 'MultiTenant';
|
||||||
@@ -85,7 +92,8 @@ export async function runTeamsChannel(_displayName: string): Promise<void> {
|
|||||||
|
|
||||||
printIntro();
|
printIntro();
|
||||||
|
|
||||||
await confirmPrereqs({ collected, completed });
|
const prereqsResult = await confirmPrereqs({ collected, completed });
|
||||||
|
if (prereqsResult === 'back') return BACK_TO_CHANNEL_SELECTION;
|
||||||
await stepPublicUrl({ collected, completed });
|
await stepPublicUrl({ collected, completed });
|
||||||
await stepAppRegistration({ collected, completed });
|
await stepAppRegistration({ collected, completed });
|
||||||
await stepClientSecret({ collected, completed });
|
await stepClientSecret({ collected, completed });
|
||||||
@@ -116,7 +124,7 @@ function printIntro(): void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmPrereqs(args: { collected: Collected; completed: string[] }): Promise<void> {
|
async function confirmPrereqs(args: { collected: Collected; completed: string[] }): Promise<'continue' | 'back'> {
|
||||||
note(
|
note(
|
||||||
[
|
[
|
||||||
'Before we start, confirm you have:',
|
'Before we start, confirm you have:',
|
||||||
@@ -131,13 +139,36 @@ async function confirmPrereqs(args: { collected: Collected; completed: string[]
|
|||||||
'Prereqs',
|
'Prereqs',
|
||||||
);
|
);
|
||||||
|
|
||||||
await stepGate({
|
// Back-aware variant of stepGate — Back is only offered on the very first
|
||||||
stepName: 'teams-prereqs',
|
// step of the Teams flow so users can bail out before any state is taken.
|
||||||
stepDescription: 'confirming they have the right Microsoft 365 tenant and tunnel',
|
while (true) {
|
||||||
reshow: () => confirmPrereqs(args),
|
const choice = ensureAnswer(
|
||||||
args,
|
await brightSelect<'done' | 'help' | 'reshow' | 'back'>({
|
||||||
});
|
message: 'How did that go?',
|
||||||
|
options: [
|
||||||
|
{ 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 === 'back') return 'back';
|
||||||
|
if (choice === 'done') break;
|
||||||
|
if (choice === 'help') {
|
||||||
|
await offerHandoff({
|
||||||
|
step: 'teams-prereqs',
|
||||||
|
stepDescription: 'confirming they have the right Microsoft 365 tenant and tunnel',
|
||||||
|
args,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (choice === 'reshow') {
|
||||||
|
return confirmPrereqs(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
args.completed.push('Prereqs confirmed.');
|
args.completed.push('Prereqs confirmed.');
|
||||||
|
return 'continue';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── step: public URL ──────────────────────────────────────────────────
|
// ─── step: public URL ──────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user