Merge pull request #2269 from alipgoldberg/setup-channel-back-nav-pr1

setup: add ← Back option to Discord, WhatsApp, iMessage channel flows
This commit is contained in:
gavrielc
2026-05-05 23:34:33 +03:00
committed by GitHub
5 changed files with 104 additions and 60 deletions

View File

@@ -27,6 +27,7 @@ import * as p from '@clack/prompts';
import k from 'kleur';
import * as setupLog from '../logs.js';
import { BACK_TO_CHANNEL_SELECTION, type ChannelFlowResult } from '../lib/back-nav.js';
import { brightSelect } from '../lib/bright-select.js';
import { confirmThenOpen, formatNoteLink } from '../lib/browser.js';
import { askOperatorRole } from '../lib/role-prompt.js';
@@ -48,8 +49,10 @@ interface AppInfo {
owner: { id: string; username: string } | null;
}
export async function runDiscordChannel(displayName: string): Promise<void> {
const hasBot = await askHasBotToken();
export async function runDiscordChannel(displayName: string): Promise<ChannelFlowResult> {
const choice = await askHasBotToken();
if (choice === 'back') return BACK_TO_CHANNEL_SELECTION;
const hasBot = choice === 'yes';
if (!hasBot) {
await walkThroughBotCreation();
}
@@ -142,17 +145,18 @@ export async function runDiscordChannel(displayName: string): Promise<void> {
}
}
async function askHasBotToken(): Promise<boolean> {
async function askHasBotToken(): Promise<'yes' | 'no' | 'back'> {
const answer = ensureAnswer(
await brightSelect({
message: 'Do you already have a Discord bot?',
options: [
{ value: 'yes', label: 'Yes, I have a bot token ready' },
{ value: 'no', label: "No, walk me through creating one" },
{ value: 'back', label: '← Back to channel selection' },
],
}),
);
return answer === 'yes';
return answer as 'yes' | 'no' | 'back';
}
async function walkThroughBotCreation(): Promise<void> {

View File

@@ -33,6 +33,7 @@ import * as p from '@clack/prompts';
import k from 'kleur';
import * as setupLog from '../logs.js';
import { BACK_TO_CHANNEL_SELECTION, type ChannelFlowResult } from '../lib/back-nav.js';
import { brightSelect } from '../lib/bright-select.js';
import { askOperatorRole } from '../lib/role-prompt.js';
import { ensureAnswer, fail, runQuietChild } from '../lib/runner.js';
@@ -48,10 +49,11 @@ interface RemoteCreds {
apiKey: string;
}
export async function runIMessageChannel(displayName: string): Promise<void> {
export async function runIMessageChannel(displayName: string): Promise<ChannelFlowResult> {
const isMac = os.platform() === 'darwin';
const mode = await askMode(isMac);
if (mode === 'back') return BACK_TO_CHANNEL_SELECTION;
let remoteCreds: RemoteCreds | null = null;
if (mode === 'local') {
@@ -139,34 +141,38 @@ export async function runIMessageChannel(displayName: string): Promise<void> {
}
}
async function askMode(isMac: boolean): Promise<Mode> {
async function askMode(isMac: boolean): Promise<Mode | 'back'> {
const baseOptions = isMac
? [
{
value: 'local' as const,
label: 'Local (this Mac)',
hint: "uses this machine's iMessage account",
},
{
value: 'remote' as const,
label: 'Remote (Photon API)',
hint: 'the bot lives on another server',
},
]
: [
{
value: 'remote' as const,
label: 'Remote (Photon API)',
hint: 'only option off macOS',
},
];
const choice = ensureAnswer(
await brightSelect<Mode>({
await brightSelect<Mode | 'back'>({
message: 'How should iMessage run?',
initialValue: isMac ? 'local' : 'remote',
options: isMac
? [
{
value: 'local',
label: 'Local (this Mac)',
hint: "uses this machine's iMessage account",
},
{
value: 'remote',
label: 'Remote (Photon API)',
hint: 'the bot lives on another server',
},
]
: [
{
value: 'remote',
label: 'Remote (Photon API)',
hint: 'only option off macOS',
},
],
options: [
...baseOptions,
{ value: 'back', label: '← Back to channel selection' },
],
}),
);
setupLog.userInput('imessage_mode', String(choice));
if (choice !== 'back') setupLog.userInput('imessage_mode', String(choice));
return choice;
}

View File

@@ -33,6 +33,7 @@ import * as p from '@clack/prompts';
import k from 'kleur';
import * as setupLog from '../logs.js';
import { BACK_TO_CHANNEL_SELECTION, type ChannelFlowResult } from '../lib/back-nav.js';
import { brightSelect } from '../lib/bright-select.js';
import { getLaunchdLabel, getSystemdUnit } from '../../src/install-slug.js';
import {
@@ -53,8 +54,9 @@ const AUTH_CREDS_PATH = path.join(process.cwd(), 'store', 'auth', 'creds.json');
type AuthMethod = 'qr' | 'pairing-code';
export async function runWhatsAppChannel(displayName: string): Promise<void> {
export async function runWhatsAppChannel(displayName: string): Promise<ChannelFlowResult> {
const method = await askAuthMethod();
if (method === 'back') return BACK_TO_CHANNEL_SELECTION;
const phone = method === 'pairing-code' ? await askPhoneNumber() : undefined;
const install = await runQuietChild(
@@ -148,7 +150,7 @@ export async function runWhatsAppChannel(displayName: string): Promise<void> {
}
}
async function askAuthMethod(): Promise<AuthMethod> {
async function askAuthMethod(): Promise<AuthMethod | 'back'> {
const choice = ensureAnswer(
await brightSelect({
message: 'How would you like to authenticate with WhatsApp?',
@@ -163,10 +165,14 @@ async function askAuthMethod(): Promise<AuthMethod> {
label: 'Enter a pairing code on your phone',
hint: 'no camera needed',
},
{
value: 'back',
label: '← Back to channel selection',
},
],
}),
) as AuthMethod;
setupLog.userInput('whatsapp_auth_method', choice);
) as AuthMethod | 'back';
if (choice !== 'back') setupLog.userInput('whatsapp_auth_method', choice);
return choice;
}