import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Box, Text, render, useApp, useInput, useStdout } from 'ink';
import { useCallback, useEffect, useRef, useState } from 'react';
import { getProviderDisplayName, getProviderSetupHint, isExperimentalProvider, isOAuthAccountProvider, providerRequiresApiKey, YAGR_SELECTABLE_MODEL_PROVIDERS, } from '../llm/provider-registry.js';
import { YagrConfigService } from '../config/yagr-config-service.js';
// ─── Palette ──────────────────────────────────────────────────────────────────
const CURSOR = '▸';
const CHECK = '✓';
const DOT = '·';
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
const PROVIDER_WIZARD_ORDER = [
    'openai',
    'openai-compatible',
    'openai-oauth',
    'anthropic',
    'anthropic-proxy',
    'google',
    'copilot-proxy',
    'mistral',
    'minimax',
    'openrouter',
];
const SELECTABLE_PROVIDER_SET = new Set(YAGR_SELECTABLE_MODEL_PROVIDERS);
const VALID_PROVIDERS = PROVIDER_WIZARD_ORDER.filter((provider) => SELECTABLE_PROVIDER_SET.has(provider));
function isHttpUrl(value) {
    return /^https?:\/\//.test(value);
}
function formatTerminalLink(url) {
    return `\u001B]8;;${url}\u0007${url}\u001B]8;;\u0007`;
}
const SURFACE_OPTIONS = [
    { value: 'telegram', label: 'Telegram', hint: 'Bot-based chat gateway' },
];
export function runSetupWizard(callbacks, options = {}) {
    return new Promise((resolve) => {
        const { unmount } = render(_jsx(SetupWizard, { callbacks: callbacks, options: options, onDone: (result) => { unmount(); resolve(result); } }));
    });
}
function sectionFor(phase) {
    if (phase.kind.startsWith('n8n'))
        return 'n8n  ·  Orchestrator';
    if (phase.kind.startsWith('llm'))
        return 'LLM  ·  Language Model';
    if (phase.kind.startsWith('surfaces') || phase.kind.startsWith('telegram'))
        return 'Gateways  ·  Messaging';
    return '';
}
function sectionIndex(phase) {
    if (phase.kind.startsWith('n8n'))
        return 1;
    if (phase.kind.startsWith('llm'))
        return 2;
    return 3;
}
function getProviderAuthCopy(provider) {
    if (provider === 'openai-oauth') {
        return {
            title: 'Connect OpenAI account',
            body: [
                'Yagr will open your browser to sign you in with your ChatGPT account.',
                'This uses your ChatGPT subscription — no API credits are consumed.',
            ],
            continueLabel: 'Sign in with ChatGPT',
        };
    }
    if (provider === 'anthropic-proxy') {
        return {
            title: 'Connect Claude token',
            body: [
                'Generate a setup-token on a machine where Claude CLI is installed and logged in:',
                '`claude setup-token`',
                'Paste the generated setup-token below.',
            ],
            continueLabel: 'Paste setup-token',
        };
    }
    if (provider === 'copilot-proxy') {
        return {
            title: 'Connect GitHub Copilot account',
            body: [
                'Yagr runs a native GitHub device login and exchanges it for a Copilot runtime token.',
                'It will show a verification URL and code in the terminal.',
            ],
            continueLabel: 'Continue with GitHub sign-in',
        };
    }
    return {
        title: `Connect ${getProviderDisplayName(provider)}`,
        body: ['Yagr will verify your account session before loading models.'],
        continueLabel: 'Continue',
    };
}
// ─── Primitive UI components ──────────────────────────────────────────────────
function Rule() {
    return _jsx(Text, { dimColor: true, children: '─'.repeat(56) });
}
function WizardInstructionLine({ line }) {
    if (!isHttpUrl(line)) {
        return _jsxs(Text, { dimColor: true, children: ["  ", line] });
    }
    return (_jsxs(Text, { color: "cyan", children: ['  ', formatTerminalLink(line)] }));
}
function Header({ phase, mode }) {
    const section = sectionFor(phase);
    const idx = sectionIndex(phase);
    const isDone = phase.kind === 'done' || phase.kind === 'cancelled' || phase.kind === 'error';
    return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: "cyan", bold: true, children: "\u25C8  Yagr Setup" }), !isDone && (_jsx(Text, { dimColor: true, children: mode === 'llm-only' || mode === 'proxy-only' || mode === 'n8n-only'
                            ? '●  step 1 / 1'
                            : `${idx === 1 ? '●' : '○'}${idx === 2 ? '●' : '○'}${idx === 3 ? '●' : '○'}  step ${idx} / 3` }))] }), section ? (_jsx(Text, { color: "cyan", dimColor: true, children: section })) : null, _jsx(Rule, {})] }));
}
function HintBar({ hints }) {
    return (_jsxs(Box, { marginTop: 1, children: [_jsx(Rule, {}), _jsx(Box, { children: hints.map((hint, i) => (_jsxs(Text, { dimColor: true, children: [hint, i < hints.length - 1 ? '   ' : ''] }, i))) })] }));
}
function FieldLabel({ label, done }) {
    return (_jsx(Box, { marginBottom: 0, children: _jsxs(Text, { color: done ? 'green' : 'white', bold: true, children: [done ? `${CHECK} ` : `${CURSOR} `, label] }) }));
}
function ErrorLine({ message }) {
    return (_jsx(Box, { marginTop: 0, children: _jsxs(Text, { color: "red", children: ["  \u2715 ", message] }) }));
}
function SpinnerDisplay({ message, frame }) {
    return (_jsxs(Box, { children: [_jsxs(Text, { color: "cyan", children: [SPINNER_FRAMES[frame % SPINNER_FRAMES.length], " "] }), _jsx(Text, { dimColor: true, children: message })] }));
}
function sanitizeTerminalInputChunk(input) {
    return input
        .replace(/\u001B\[200~/g, '')
        .replace(/\u001B\[201~/g, '')
        .replace(/\r/g, '');
}
function getDisplayedModelOptions(models) {
    return [...models, '__custom__'];
}
function getVisibleWindow(total, cursor, maxVisible) {
    if (total <= maxVisible) {
        return { start: 0, end: total };
    }
    const clampedCursor = Math.max(0, Math.min(cursor, total - 1));
    const half = Math.floor(maxVisible / 2);
    let start = Math.max(0, clampedCursor - half);
    const maxStart = Math.max(0, total - maxVisible);
    if (start > maxStart) {
        start = maxStart;
    }
    return { start, end: Math.min(total, start + maxVisible) };
}
function getListViewportHeight(terminalRows, reservedRows) {
    return Math.max(2, terminalRows - reservedRows - 2);
}
function truncateTerminalLine(value, maxWidth) {
    if (maxWidth <= 0) {
        return '';
    }
    if (value.length <= maxWidth) {
        return value;
    }
    if (maxWidth === 1) {
        return '…';
    }
    return `${value.slice(0, maxWidth - 1)}…`;
}
function WizardTextInput({ value, onChange, onSubmit, placeholder, mask, }) {
    const valueRef = useRef(value);
    const cursorOffsetRef = useRef(value.length);
    const [renderState, setRenderState] = useState({
        value,
        cursorOffset: value.length,
    });
    useEffect(() => {
        valueRef.current = value;
        cursorOffsetRef.current = Math.min(cursorOffsetRef.current, value.length);
        setRenderState({
            value,
            cursorOffset: Math.min(cursorOffsetRef.current, value.length),
        });
    }, [value]);
    useInput((input, key) => {
        if (key.upArrow || key.downArrow || (key.ctrl && input === 'c') || key.tab || (key.shift && key.tab)) {
            return;
        }
        const currentValue = valueRef.current;
        const currentCursorOffset = cursorOffsetRef.current;
        if (key.return) {
            onSubmit(currentValue);
            return;
        }
        let nextValue = currentValue;
        let nextCursorOffset = currentCursorOffset;
        if (key.leftArrow) {
            nextCursorOffset = Math.max(0, currentCursorOffset - 1);
        }
        else if (key.rightArrow) {
            nextCursorOffset = Math.min(currentValue.length, currentCursorOffset + 1);
        }
        else if (key.backspace || key.delete) {
            if (currentCursorOffset > 0) {
                nextValue = currentValue.slice(0, currentCursorOffset - 1) + currentValue.slice(currentCursorOffset);
                nextCursorOffset = currentCursorOffset - 1;
            }
        }
        else {
            const sanitizedInput = sanitizeTerminalInputChunk(input);
            if (!sanitizedInput) {
                return;
            }
            nextValue = currentValue.slice(0, currentCursorOffset) + sanitizedInput + currentValue.slice(currentCursorOffset);
            nextCursorOffset = currentCursorOffset + sanitizedInput.length;
        }
        valueRef.current = nextValue;
        cursorOffsetRef.current = nextCursorOffset;
        setRenderState({ value: nextValue, cursorOffset: nextCursorOffset });
        if (nextValue !== currentValue) {
            onChange(nextValue);
        }
    });
    const displayValue = mask ? mask.repeat(renderState.value.length) : renderState.value;
    if (displayValue.length === 0) {
        return _jsx(Text, { dimColor: true, children: placeholder ?? '' });
    }
    if (renderState.cursorOffset >= displayValue.length) {
        return _jsxs(Text, { children: [displayValue, _jsx(Text, { inverse: true, children: " " })] });
    }
    return (_jsxs(Text, { children: [displayValue.slice(0, renderState.cursorOffset), _jsx(Text, { inverse: true, children: displayValue.charAt(renderState.cursorOffset) }), displayValue.slice(renderState.cursorOffset + 1)] }));
}
function SelectList({ options, cursor, getLabel, getHint, maxVisibleRows, maxLineWidth, }) {
    const visibleRows = Math.max(1, maxVisibleRows ?? options.length);
    const { start, end } = getVisibleWindow(options.length, cursor, visibleRows);
    const visibleOptions = options.slice(start, end);
    const availableWidth = maxLineWidth ?? Number.MAX_SAFE_INTEGER;
    return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [start > 0 ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(`  ↑  ${start} more`, availableWidth) }) : null, visibleOptions.map((opt, visibleIndex) => {
                const i = start + visibleIndex;
                const active = i === cursor;
                const hint = getHint?.(opt);
                const prefix = active ? `  ${CURSOR} ` : '    ';
                const line = truncateTerminalLine(formatOptionLineWithHint(prefix, getLabel(opt), hint, availableWidth), availableWidth);
                return (_jsx(Box, { children: _jsx(Text, { color: active ? 'cyan' : undefined, bold: active, children: line }) }, i));
            }), end < options.length ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(`  ↓  ${options.length - end} more`, availableWidth) }) : null] }));
}
function formatOptionLineWithHint(prefix, label, hint, width) {
    if (!hint) {
        return `${prefix}${label}`;
    }
    return `${prefix}${label}  ${DOT}  ${hint}`;
}
function MultiSelectList({ options, cursor, selected, maxVisibleRows, maxLineWidth, }) {
    const visibleRows = Math.max(1, maxVisibleRows ?? options.length);
    const { start, end } = getVisibleWindow(options.length, cursor, visibleRows);
    const visibleOptions = options.slice(start, end);
    const availableWidth = maxLineWidth ?? Number.MAX_SAFE_INTEGER;
    return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [start > 0 ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(`  ↑  ${start} more`, availableWidth) }) : null, visibleOptions.map((opt, visibleIndex) => {
                const i = start + visibleIndex;
                const active = i === cursor;
                const checked = selected.includes(opt.value);
                const prefix = active ? `  ${CURSOR} ` : '    ';
                const checkbox = checked ? '☑' : '☐';
                const line = truncateTerminalLine(`${prefix}${checkbox}  ${opt.label}  ${DOT}  ${opt.hint}`, availableWidth);
                return (_jsx(Box, { children: _jsx(Text, { color: active || checked ? 'cyan' : undefined, bold: active, children: line }) }, opt.value));
            }), end < options.length ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(`  ↓  ${options.length - end} more`, availableWidth) }) : null] }));
}
// ─── Main wizard component ────────────────────────────────────────────────────
function SetupWizard({ callbacks, options, onDone }) {
    const app = useApp();
    const { stdout } = useStdout();
    const n8nDef = callbacks.getN8nDefaults();
    const llmDef = callbacks.getLlmDefaults();
    const surfDef = callbacks.getSurfaceDefaults();
    const mode = options?.mode ?? 'full';
    const [phase, setPhase] = useState(() => {
        if (mode === 'proxy-only' || (mode === 'llm-only' && !callbacks.isLlmProxyEnabled() && n8nDef.projectId)) {
            const n8nUrl = n8nDef.url;
            return { kind: 'llm-proxy-setup', url: n8nUrl, instanceProfile: n8nDef.instanceProfile, status: 'detecting', cursor: 0 };
        }
        if (mode === 'llm-only') {
            const llmProvider = llmDef.provider;
            if (llmProvider) {
                const existingApiKey = llmDef.getApiKey(llmProvider);
                const existingModel = llmDef.getDefaultModel(llmProvider);
                if (existingModel && (existingApiKey || !providerRequiresApiKey(llmProvider))) {
                    return { kind: 'llm-reuse-config', provider: llmProvider, apiKey: existingApiKey ?? '', model: existingModel, cursor: 0 };
                }
                return {
                    kind: 'llm-provider',
                    initial: llmProvider,
                    cursor: Math.max(0, VALID_PROVIDERS.indexOf(llmProvider)),
                };
            }
            return { kind: 'llm-provider', cursor: 0 };
        }
        return { kind: 'n8n-mode', cursor: 0 };
    });
    const [textValue, setTextValue] = useState('');
    const [spinnerFrame, setSpinnerFrame] = useState(0);
    const asyncGuard = useRef(0);
    const llmApiKeyDraftsRef = useRef({});
    const llmBaseUrlDraftsRef = useRef({});
    const terminalRows = stdout?.rows ?? process.stdout.rows ?? 24;
    const terminalColumns = stdout?.columns ?? process.stdout.columns ?? 80;
    const listLineWidth = Math.max(12, terminalColumns - 6);
    const isLoading = phase.kind === 'n8n-connecting' || phase.kind === 'n8n-saving' || phase.kind === 'n8n-local-installing'
        || phase.kind === 'llm-models-loading' || phase.kind === 'telegram-connecting'
        || (phase.kind === 'llm-proxy-setup' && (phase.status === 'detecting' || phase.status === 'provisioning'));
    useEffect(() => {
        if (!isLoading)
            return;
        const id = setInterval(() => setSpinnerFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
        return () => clearInterval(id);
    }, [isLoading]);
    const cancel = useCallback((reason) => {
        setPhase({ kind: 'cancelled', reason });
        setTimeout(() => { onDone({ ok: false }); app.exit(); }, 500);
    }, [app, onDone]);
    useInput((_input, key) => {
        if (key.ctrl && _input === 'c') {
            cancel('Setup cancelled.');
        }
    });
    useEffect(() => {
        if (phase.kind !== 'n8n-local-installing')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                const state = await callbacks.installManagedLocalN8n('docker');
                if (guard !== asyncGuard.current)
                    return;
                const bootstrap = await callbacks.bootstrapManagedLocalN8n(state.url);
                if (guard !== asyncGuard.current)
                    return;
                const instanceProfile = 'yagr-managed-docker';
                if (bootstrap.mode === 'silent' && bootstrap.apiKey) {
                    setPhase({ kind: 'n8n-connecting', url: state.url, apiKey: bootstrap.apiKey, instanceProfile });
                    return;
                }
                const existing = callbacks.getN8nDefaults(state.url).apiKey;
                if (existing) {
                    setPhase({ kind: 'n8n-connecting', url: state.url, apiKey: existing, instanceProfile });
                    return;
                }
                setPhase({
                    kind: 'n8n-local-ready',
                    url: state.url,
                    instanceProfile,
                    cursor: 0,
                    note: bootstrap.reason ? `Silent bootstrap fallback: ${bootstrap.reason}` : undefined,
                });
            }
            catch (err) {
                if (guard !== asyncGuard.current)
                    return;
                setPhase({
                    kind: 'n8n-mode',
                    cursor: 0,
                    err: err instanceof Error ? err.message : String(err),
                });
            }
        })();
    }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => {
        if (phase.kind !== 'n8n-docker-check')
            return;
        const currentPhase = phase;
        if (currentPhase.status !== 'checking')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                const assessment = await import('../n8n-local/detect.js').then((m) => m.inspectLocalN8nBootstrap());
                if (guard !== asyncGuard.current)
                    return;
                const dockerAvailable = assessment.docker.available && assessment.docker.reachable !== false;
                setPhase((p) => ({
                    ...p,
                    status: dockerAvailable ? 'available' : 'unavailable',
                }));
            }
            catch {
                if (guard !== asyncGuard.current)
                    return;
                setPhase((p) => ({
                    ...p,
                    status: 'unavailable',
                }));
            }
        })();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [phase.kind, phase.status]);
    useEffect(() => {
        if (phase.kind !== 'n8n-docker-check')
            return;
        const currentPhase = phase;
        if (currentPhase.status !== 'available')
            return;
        setPhase({ kind: 'n8n-local-installing', startedAt: Date.now() });
    }, [phase.kind, phase.status]); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => {
        if (phase.kind !== 'n8n-connecting')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                const projects = await callbacks.testN8nConnection(phase.url, phase.apiKey);
                if (guard !== asyncGuard.current)
                    return;
                if (projects.length === 1) {
                    setPhase({
                        kind: 'n8n-syncfolder',
                        url: phase.url, apiKey: phase.apiKey, instanceProfile: phase.instanceProfile,
                        project: projects[0],
                        def: n8nDef.syncFolder ?? 'workflows',
                    });
                    setTextValue(n8nDef.syncFolder ?? 'workflows');
                }
                else {
                    setPhase({ kind: 'n8n-project', url: phase.url, apiKey: phase.apiKey, instanceProfile: phase.instanceProfile, projects, cursor: 0 });
                }
            }
            catch (err) {
                if (guard !== asyncGuard.current)
                    return;
                setPhase({ kind: 'n8n-url', def: phase.url, err: err.message });
                setTextValue(phase.url);
            }
        })();
    }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => {
        if (phase.kind !== 'n8n-saving')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                await callbacks.saveN8nConfig({ url: phase.url, apiKey: phase.apiKey, project: phase.project, syncFolder: phase.syncFolder, instanceProfile: phase.instanceProfile });
                if (guard !== asyncGuard.current)
                    return;
                if (mode === 'n8n-only') {
                    setPhase({
                        kind: 'done',
                        n8nHost: phase.url,
                        n8nProject: phase.project.name ?? phase.project.id,
                        provider: '',
                        model: '',
                        surfaces: [],
                    });
                    setTimeout(() => { onDone({ ok: true }); app.exit(); }, 250);
                    return;
                }
                // After n8n config is saved, offer tunnel setup if this is a Yagr-managed local instance
                const isYagrManaged = phase.instanceProfile === 'yagr-managed-docker' || phase.instanceProfile === 'yagr-managed-direct';
                const tunnelCfg = new YagrConfigService().getN8nTunnelConfig();
                if (isYagrManaged && !tunnelCfg?.enabled) {
                    setPhase({ kind: 'n8n-tunnel-offer', url: phase.url, instanceProfile: phase.instanceProfile, cursor: 0, status: 'offer' });
                }
                else {
                    setPhase({ kind: 'llm-proxy-setup', url: phase.url, instanceProfile: phase.instanceProfile, status: 'detecting', cursor: 0 });
                }
            }
            catch (err) {
                if (guard !== asyncGuard.current)
                    return;
                setPhase({ kind: 'error', message: err.message });
                setTimeout(() => { onDone({ ok: false }); app.exit(); }, 2000);
            }
        })();
    }, [phase.kind, mode]); // eslint-disable-line react-hooks/exhaustive-deps
    // ── llm-proxy-setup: auto-detect and start the relay ──────────────────────
    useEffect(() => {
        if (phase.kind !== 'llm-proxy-setup' || phase.status !== 'detecting')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                const result = await callbacks.setupLlmProxy(phase.url, phase.instanceProfile);
                if (guard !== asyncGuard.current)
                    return;
                setPhase((p) => ({
                    ...p,
                    status: 'ready',
                    mode: result.mode,
                    relayUrl: result.credentialBaseUrl,
                }));
            }
            catch (err) {
                if (guard !== asyncGuard.current)
                    return;
                setPhase((p) => ({
                    ...p,
                    status: 'failed',
                    err: err.message,
                }));
            }
        })();
    }, [phase.kind, phase.status]); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => {
        if (phase.kind !== 'llm-models-loading')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                let models = [];
                let resolvedApiKey = phase.apiKey;
                let note = phase.note;
                const prepared = await callbacks.prepareProvider(phase.provider, phase.apiKey || undefined);
                if (guard !== asyncGuard.current)
                    return;
                if (prepared.ready) {
                    if (prepared.baseUrl) {
                        llmBaseUrlDraftsRef.current[phase.provider] = prepared.baseUrl;
                    }
                    if (prepared.apiKey !== undefined) {
                        resolvedApiKey = prepared.apiKey;
                        llmApiKeyDraftsRef.current[phase.provider] = prepared.apiKey;
                    }
                    if (prepared.notes && prepared.notes.length > 0) {
                        note = prepared.notes.join(' ');
                    }
                    models = prepared.models ?? [];
                }
                else if (isOAuthAccountProvider(phase.provider)) {
                    // OAuth provider failed — redirect to auth flow.
                    setTextValue('');
                    setPhase({ kind: 'llm-account-auth', provider: phase.provider, cursor: 0 });
                    return;
                }
                if (models.length === 0) {
                    models = await callbacks.fetchModels(phase.provider, resolvedApiKey || undefined);
                }
                if (guard !== asyncGuard.current)
                    return;
                const displayedOptions = getDisplayedModelOptions(models);
                const idx = phase.defModel ? displayedOptions.indexOf(phase.defModel) : -1;
                setPhase({
                    kind: 'llm-model',
                    provider: phase.provider, apiKey: resolvedApiKey,
                    models, defModel: phase.defModel,
                    cursor: idx >= 0 ? idx : 0,
                    note,
                });
            }
            catch {
                if (guard !== asyncGuard.current)
                    return;
                setPhase({
                    kind: 'llm-model',
                    provider: phase.provider, apiKey: phase.apiKey,
                    models: [], defModel: phase.defModel, cursor: 0, note: phase.note,
                });
            }
        })();
    }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => {
        if (phase.kind !== 'telegram-connecting')
            return;
        const guard = ++asyncGuard.current;
        void (async () => {
            try {
                const identity = await callbacks.setupTelegram(phase.token);
                if (guard !== asyncGuard.current)
                    return;
                callbacks.saveSurfaces({
                    surfaces: phase.surfaces,
                    telegram: { token: phase.token, username: identity.username },
                });
                const telegramDeepLink = `https://t.me/${identity.username}`;
                setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces: phase.surfaces, telegramDeepLink });
                setTimeout(() => { onDone({ ok: true, telegramDeepLink }); app.exit(); }, 800);
            }
            catch (err) {
                if (guard !== asyncGuard.current)
                    return;
                setPhase({ kind: 'telegram-token', surfaces: phase.surfaces, err: err.message });
                setTextValue('');
            }
        })();
    }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
    const handleN8nUrlSubmit = useCallback((value) => {
        const url = value.trim().replace(/^['"]|['"]$/g, '');
        if (!url) {
            setPhase((p) => ({ ...p, err: 'A URL is required.' }));
            return;
        }
        try {
            new URL(url);
        }
        catch {
            setPhase((p) => ({ ...p, err: 'Enter a valid URL.' }));
            return;
        }
        const existing = callbacks.getN8nDefaults(url).apiKey;
        if (existing) {
            setPhase({ kind: 'n8n-reuse-apikey', url, existing, cursor: 0 });
        }
        else {
            setPhase({ kind: 'n8n-apikey', url });
            setTextValue('');
        }
    }, [callbacks]);
    const handleN8nApiKeySubmit = useCallback((url) => (value) => {
        const key = value.trim();
        if (!key) {
            setPhase((p) => ({ ...p, err: 'API key is required.' }));
            return;
        }
        setPhase({ kind: 'n8n-instance-location', url, apiKey: key, cursor: 0 });
    }, []);
    const handleSyncFolderSubmit = useCallback((url, apiKey, project, instanceProfile) => (value) => {
        const folder = value.trim();
        if (!folder) {
            setPhase((p) => ({ ...p, err: 'Sync folder is required.' }));
            return;
        }
        setPhase({ kind: 'n8n-saving', url, apiKey, instanceProfile, project, syncFolder: folder });
    }, []);
    const handleBaseUrlSubmit = useCallback((provider, apiKey, model, reasoningEffort) => (value) => {
        const url = value.trim();
        if (url) {
            try {
                new URL(url);
            }
            catch {
                setPhase((p) => ({ ...p, err: 'Enter a valid URL.' }));
                return;
            }
        }
        callbacks.saveLlmConfig({ provider, apiKey, model, baseUrl: url || undefined, reasoningEffort });
        const currentSurfaces = surfDef.surfaces;
        setPhase({ kind: 'surfaces', cursor: 0, selected: currentSurfaces });
    }, [callbacks, surfDef]);
    const handleTelegramTokenSubmit = useCallback((surfaces) => (value) => {
        const token = value.trim();
        if (!token || !token.includes(':')) {
            setPhase((p) => ({ ...p, err: 'Enter a valid BotFather token.' }));
            return;
        }
        setPhase({ kind: 'telegram-connecting', surfaces, token });
    }, []);
    const transitionToLlmModelsLoading = useCallback((provider, apiKey, defModel, note) => {
        setTextValue('');
        setPhase({
            kind: 'llm-models-loading',
            provider,
            apiKey,
            defModel,
            ...(note ? { note } : {}),
        });
    }, []);
    const handleLlmApiKeySubmit = useCallback((provider) => (value) => {
        const key = value.trim();
        if (!key && providerRequiresApiKey(provider)) {
            setPhase((p) => ({ ...p, err: 'API key is required.' }));
            return;
        }
        llmApiKeyDraftsRef.current[provider] = key;
        const defModel = llmDef.getDefaultModel(provider);
        transitionToLlmModelsLoading(provider, key, defModel);
    }, [llmDef, transitionToLlmModelsLoading]);
    const handleAccountAuthSubmit = useCallback((provider, state) => (value) => {
        void (async () => {
            try {
                const result = await callbacks.completeAccountAuth(provider, value, state);
                if (!result.ok) {
                    setPhase((current) => current.kind === 'llm-account-input'
                        ? { ...current, err: result.error || 'Authentication failed.' }
                        : current);
                    return;
                }
                const defModel = llmDef.getDefaultModel(provider);
                transitionToLlmModelsLoading(provider, result.apiKey ?? '', defModel);
            }
            catch (error) {
                setPhase((current) => current.kind === 'llm-account-input'
                    ? { ...current, err: error instanceof Error ? error.message : String(error) }
                    : current);
            }
        })();
    }, [callbacks, llmDef, transitionToLlmModelsLoading]);
    const saveLlmAndContinue = useCallback((provider, apiKey, model, note) => {
        if (provider === 'openai-oauth') {
            const currentEffort = llmDef.provider === provider ? llmDef.reasoningEffort : undefined;
            const effortOptions = ['low', 'medium', 'high'];
            const selectedCursor = Math.max(0, effortOptions.indexOf((currentEffort === 'low' || currentEffort === 'medium' || currentEffort === 'high') ? currentEffort : 'medium'));
            setPhase({ kind: 'llm-reasoning-effort', provider, apiKey, model, cursor: selectedCursor });
            return;
        }
        const draftedBaseUrl = llmBaseUrlDraftsRef.current[provider];
        callbacks.saveLlmConfig({ provider, apiKey, model, baseUrl: draftedBaseUrl || undefined });
        setTextValue('');
        if (mode === 'llm-only') {
            setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider, model, surfaces: surfDef.surfaces });
            setTimeout(() => { onDone({ ok: true }); app.exit(); }, 250);
            return;
        }
        setPhase({ kind: 'surfaces', cursor: 0, selected: surfDef.surfaces });
    }, [app, callbacks, llmDef, mode, onDone, surfDef]);
    const handleSelectKey = useCallback((input, key) => {
        if (phase.kind === 'n8n-mode') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1), err: undefined });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1), err: undefined });
            else if (key.return) {
                if (phase.cursor === 0) {
                    setPhase({ kind: 'n8n-docker-check', status: 'checking', cursor: 0, err: undefined });
                }
                else {
                    setPhase({ kind: 'n8n-url', def: n8nDef.url });
                    setTextValue(n8nDef.url);
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-docker-check') {
            if (phase.status === 'unavailable') {
                if (key.upArrow)
                    setPhase((p) => ({ ...p, cursor: Math.max(0, p.cursor - 1) }));
                else if (key.downArrow)
                    setPhase((p) => ({ ...p, cursor: Math.min(1, p.cursor + 1) }));
                else if (key.return) {
                    if (phase.cursor === 0) {
                        setPhase({ kind: 'n8n-docker-check', status: 'checking', cursor: 0, err: undefined });
                    }
                    else {
                        setPhase({ kind: 'n8n-mode', cursor: 0, err: undefined });
                    }
                }
                else if (key.escape)
                    cancel('Setup cancelled.');
            }
            // 'available' status is handled by a separate useEffect that auto-proceeds
        }
        else if (phase.kind === 'n8n-local-ready') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    void (async () => {
                        try {
                            await callbacks.openUrl(phase.url);
                        }
                        catch {
                            // Leave the flow available even if browser opening fails.
                        }
                        const existing = callbacks.getN8nDefaults(phase.url).apiKey;
                        if (existing) {
                            setPhase({ kind: 'n8n-connecting', url: phase.url, apiKey: existing, instanceProfile: phase.instanceProfile });
                            return;
                        }
                        setPhase({
                            kind: 'n8n-local-auth',
                            url: phase.url,
                            message: 'The local n8n editor is ready. Create the owner account, then open Settings > n8n API, generate a key for Yagr, and paste it here.',
                        });
                        setTextValue('');
                    })();
                }
                else {
                    setPhase({
                        kind: 'n8n-local-auth',
                        url: phase.url,
                        message: 'Open the local n8n URL in your browser, create the owner account, then generate a key in Settings > n8n API and paste it here.',
                    });
                    setTextValue('');
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-reuse-apikey') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    setPhase({ kind: 'n8n-instance-location', url: phase.url, apiKey: phase.existing, cursor: 0 });
                }
                else {
                    setPhase({ kind: 'n8n-apikey', url: phase.url });
                    setTextValue('');
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-instance-location') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    setPhase({ kind: 'n8n-connecting', url: phase.url, apiKey: phase.apiKey, instanceProfile: 'custom-cloud' });
                }
                else {
                    setPhase({ kind: 'n8n-local-runtime', url: phase.url, apiKey: phase.apiKey, cursor: 0 });
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-local-runtime') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(2, phase.cursor + 1) });
            else if (key.return) {
                setPhase({
                    kind: 'n8n-connecting',
                    url: phase.url,
                    apiKey: phase.apiKey,
                    instanceProfile: phase.cursor === 0 ? 'custom-local-docker' : 'custom-local-direct',
                });
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-project') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(phase.projects.length - 1, phase.cursor + 1) });
            else if (key.return) {
                const project = phase.projects[phase.cursor];
                setPhase({ kind: 'n8n-syncfolder', url: phase.url, apiKey: phase.apiKey, instanceProfile: phase.instanceProfile, project, def: n8nDef.syncFolder ?? 'workflows' });
                setTextValue(n8nDef.syncFolder ?? 'workflows');
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-reuse-config') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    llmApiKeyDraftsRef.current[phase.provider] = phase.apiKey;
                    const draftedBaseUrl = llmBaseUrlDraftsRef.current[phase.provider] ?? llmDef.getBaseUrl(phase.provider);
                    callbacks.saveLlmConfig({ provider: phase.provider, apiKey: phase.apiKey, model: phase.model, baseUrl: draftedBaseUrl });
                    setTextValue('');
                    setPhase({ kind: 'surfaces', cursor: 0, selected: surfDef.surfaces });
                }
                else {
                    setTextValue('');
                    setPhase({ kind: 'llm-provider', initial: phase.provider, cursor: VALID_PROVIDERS.indexOf(phase.provider) });
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-provider') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(VALID_PROVIDERS.length - 1, phase.cursor + 1) });
            else if (key.return) {
                const provider = VALID_PROVIDERS[phase.cursor];
                const existing = llmApiKeyDraftsRef.current[provider] ?? llmDef.getApiKey(provider);
                if (isOAuthAccountProvider(provider)) {
                    void (async () => {
                        const hasSession = await callbacks.hasAccountSession(provider);
                        if (hasSession) {
                            setTextValue('');
                            setPhase({ kind: 'llm-oauth-reuse', provider, cursor: 0 });
                        }
                        else {
                            setTextValue('');
                            setPhase({ kind: 'llm-account-auth', provider, cursor: 0 });
                        }
                    })();
                }
                else if (!providerRequiresApiKey(provider)) {
                    const defModel = llmDef.getDefaultModel(provider);
                    transitionToLlmModelsLoading(provider, existing ?? '', defModel);
                }
                else if (existing) {
                    setTextValue('');
                    setPhase({ kind: 'llm-reuse-apikey', provider, existing, cursor: 0 });
                }
                else {
                    setPhase({ kind: 'llm-apikey', provider });
                    setTextValue(llmApiKeyDraftsRef.current[provider] ?? '');
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-oauth-reuse') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    // Use existing auth — try to load models directly
                    const defModel = llmDef.getDefaultModel(phase.provider);
                    transitionToLlmModelsLoading(phase.provider, '', defModel);
                }
                else {
                    // Renew auth
                    setTextValue('');
                    setPhase({ kind: 'llm-account-auth', provider: phase.provider, cursor: 0 });
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-account-auth') {
            const maxCursor = 1;
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(maxCursor, phase.cursor + 1) });
            else if (key.return) {
                if (phase.provider === 'anthropic-proxy' && phase.cursor === 0) {
                    setPhase({
                        kind: 'llm-account-input',
                        provider: 'anthropic-proxy',
                        title: 'Claude setup-token',
                        instructions: [
                            'On a machine where Claude CLI is installed and logged in, run:',
                            'claude setup-token',
                            'Copy the generated setup-token and paste it below.',
                        ],
                        placeholder: 'Paste setup-token',
                        submitLabel: 'Continue with setup-token',
                        state: 'anthropic:setup-token',
                    });
                    setTextValue('');
                }
                else if (phase.cursor === 0) {
                    void (async () => {
                        try {
                            const authStep = await callbacks.startAccountAuth(phase.provider);
                            const authUrl = (authStep.instructions ?? [])
                                .map((line) => line.match(/https?:\/\/\S+/)?.[0])
                                .find(Boolean);
                            if (authUrl) {
                                void callbacks.openUrl(authUrl).catch(() => {
                                    // Browser auto-open is best effort only.
                                });
                            }
                            if (authStep.kind === 'input') {
                                setPhase({
                                    kind: 'llm-account-input',
                                    provider: phase.provider,
                                    title: authStep.title ?? getProviderAuthCopy(phase.provider).title,
                                    instructions: authStep.instructions ?? [],
                                    placeholder: authStep.placeholder,
                                    submitLabel: authStep.submitLabel ?? 'Continue',
                                    state: authStep.state,
                                });
                                setTextValue('');
                                return;
                            }
                            const defModel = llmDef.getDefaultModel(phase.provider);
                            const authCopy = getProviderAuthCopy(phase.provider);
                            transitionToLlmModelsLoading(phase.provider, '', defModel, authCopy.body.join(' '));
                        }
                        catch (error) {
                            setPhase({
                                kind: 'llm-account-input',
                                provider: phase.provider,
                                title: getProviderAuthCopy(phase.provider).title,
                                instructions: getProviderAuthCopy(phase.provider).body,
                                submitLabel: 'Continue',
                                err: error instanceof Error ? error.message : String(error),
                            });
                        }
                    })();
                }
                else {
                    setPhase({ kind: 'llm-provider', initial: phase.provider, cursor: VALID_PROVIDERS.indexOf(phase.provider) });
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-account-input') {
            if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-reuse-apikey') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    const defModel = llmDef.getDefaultModel(phase.provider);
                    llmApiKeyDraftsRef.current[phase.provider] = phase.existing;
                    transitionToLlmModelsLoading(phase.provider, phase.existing, defModel);
                }
                else {
                    setPhase({ kind: 'llm-apikey', provider: phase.provider });
                    setTextValue(llmApiKeyDraftsRef.current[phase.provider] ?? '');
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-model') {
            const allOptions = getDisplayedModelOptions(phase.models);
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(allOptions.length - 1, phase.cursor + 1) });
            else if (key.return) {
                const selected = allOptions[phase.cursor];
                if (selected === '__custom__') {
                    setPhase({ ...phase, models: [] });
                    setTextValue(phase.defModel ?? '');
                    return;
                }
                const model = selected;
                if (phase.provider === 'openai-oauth') {
                    saveLlmAndContinue(phase.provider, phase.apiKey, model, phase.note);
                    return;
                }
                const draftedBaseUrl = llmBaseUrlDraftsRef.current[phase.provider];
                const defaultBaseUrl = draftedBaseUrl ?? llmDef.getBaseUrl(phase.provider);
                const needsUrl = llmDef.needsBaseUrl(phase.provider);
                if (draftedBaseUrl) {
                    saveLlmAndContinue(phase.provider, phase.apiKey, model, phase.note);
                }
                else if (needsUrl || defaultBaseUrl) {
                    setPhase({ kind: 'llm-baseurl', provider: phase.provider, apiKey: phase.apiKey, model, def: defaultBaseUrl ?? '' });
                    setTextValue(defaultBaseUrl ?? '');
                }
                else {
                    saveLlmAndContinue(phase.provider, phase.apiKey, model, phase.note);
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-reasoning-effort') {
            const effortOptions = ['low', 'medium', 'high'];
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(effortOptions.length - 1, phase.cursor + 1) });
            else if (key.return) {
                const selectedEffort = effortOptions[phase.cursor];
                const draftedBaseUrl = llmBaseUrlDraftsRef.current[phase.provider];
                const defaultBaseUrl = draftedBaseUrl ?? llmDef.getBaseUrl(phase.provider);
                const needsUrl = llmDef.needsBaseUrl(phase.provider);
                if (draftedBaseUrl) {
                    callbacks.saveLlmConfig({ provider: phase.provider, apiKey: phase.apiKey, model: phase.model, baseUrl: draftedBaseUrl, reasoningEffort: selectedEffort });
                    setTextValue('');
                    if (mode === 'llm-only') {
                        setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: phase.provider, model: phase.model, surfaces: surfDef.surfaces });
                        setTimeout(() => { onDone({ ok: true }); app.exit(); }, 250);
                        return;
                    }
                    setPhase({ kind: 'surfaces', cursor: 0, selected: surfDef.surfaces });
                }
                else if (needsUrl || defaultBaseUrl) {
                    setPhase({ kind: 'llm-baseurl', provider: phase.provider, apiKey: phase.apiKey, model: phase.model, def: defaultBaseUrl ?? '', reasoningEffort: selectedEffort });
                    setTextValue(defaultBaseUrl ?? '');
                }
                else {
                    callbacks.saveLlmConfig({ provider: phase.provider, apiKey: phase.apiKey, model: phase.model, reasoningEffort: selectedEffort });
                    setTextValue('');
                    if (mode === 'llm-only') {
                        setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: phase.provider, model: phase.model, surfaces: surfDef.surfaces });
                        setTimeout(() => { onDone({ ok: true }); app.exit(); }, 250);
                        return;
                    }
                    setPhase({ kind: 'surfaces', cursor: 0, selected: surfDef.surfaces });
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'surfaces') {
            const opts = SURFACE_OPTIONS.map((o) => o.value);
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(opts.length - 1, phase.cursor + 1) });
            else if (input === ' ') {
                const val = opts[phase.cursor];
                const next = phase.selected.includes(val)
                    ? phase.selected.filter((s) => s !== val)
                    : [...phase.selected, val];
                setPhase({ ...phase, selected: next });
            }
            else if (key.return) {
                const surfaces = phase.selected;
                if (surfaces.includes('telegram')) {
                    const existingToken = callbacks.getTelegramToken();
                    if (existingToken) {
                        setPhase({ kind: 'telegram-reuse-token', surfaces, existing: existingToken, cursor: 0 });
                    }
                    else {
                        setPhase({ kind: 'telegram-token', surfaces });
                        setTextValue('');
                    }
                }
                else {
                    callbacks.saveSurfaces({ surfaces });
                    setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces });
                    setTimeout(() => { onDone({ ok: true }); app.exit(); }, 500);
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'telegram-reuse-token') {
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    callbacks.saveSurfaces({ surfaces: phase.surfaces });
                    setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces: phase.surfaces });
                    setTimeout(() => { onDone({ ok: true }); app.exit(); }, 500);
                }
                else {
                    setPhase({ kind: 'telegram-token', surfaces: phase.surfaces });
                    setTextValue('');
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'llm-proxy-setup' && (phase.status === 'ready' || phase.status === 'failed')) {
            const maxCursor = 1; // 0=accept/continue, 1=skip
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(maxCursor, phase.cursor + 1) });
            else if (key.return) {
                const goToLlm = () => {
                    if (mode === 'proxy-only') {
                        setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces: [] });
                        setTimeout(() => { onDone({ ok: true }); app.exit(); }, 500);
                        return;
                    }
                    const llmProvider = llmDef.provider;
                    if (llmProvider) {
                        const apiKey = llmDef.getApiKey(llmProvider);
                        const model = llmDef.getDefaultModel(llmProvider);
                        if (model && (apiKey || !providerRequiresApiKey(llmProvider))) {
                            setPhase({ kind: 'llm-reuse-config', provider: llmProvider, apiKey: apiKey ?? '', model, cursor: 0 });
                            return;
                        }
                    }
                    setPhase({ kind: 'llm-provider', initial: llmProvider, cursor: llmProvider ? VALID_PROVIDERS.indexOf(llmProvider) : 0 });
                };
                if (phase.cursor === 0 && phase.status === 'ready' && phase.mode && phase.relayUrl) {
                    // Accept: save llmProxy config then proceed
                    callbacks.saveLlmProxyConfig({
                        enabled: true,
                        mode: phase.mode,
                        credentialBaseUrl: phase.relayUrl,
                        confirmedCredentialBaseUrl: phase.relayUrl,
                        dockerHostAddress: (phase.mode === 'docker') ? phase.relayUrl.replace(/^http:\/\//, '').replace(/:\d+.*/, '') : undefined,
                        llmTunnelUrl: (phase.mode === 'tunnel') ? phase.relayUrl.replace(/\/v1$/, '') : undefined,
                    });
                    setPhase({
                        kind: 'llm-proxy-setup',
                        url: phase.url,
                        instanceProfile: phase.instanceProfile,
                        status: 'provisioning',
                        mode: phase.mode,
                        relayUrl: phase.relayUrl,
                        cursor: 0,
                    });
                    callbacks.provisionLlmProxyCredential().then(goToLlm, (error) => {
                        setPhase({
                            kind: 'llm-proxy-setup',
                            url: phase.url,
                            instanceProfile: phase.instanceProfile,
                            status: 'failed',
                            err: error instanceof Error ? error.message : String(error),
                            cursor: 0,
                        });
                    });
                }
                else {
                    // Skip
                    goToLlm();
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-tunnel-offer' && phase.status === 'offer') {
            const maxCursor = 1; // 0=yes, 1=skip
            if (key.upArrow)
                setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
            else if (key.downArrow)
                setPhase({ ...phase, cursor: Math.min(maxCursor, phase.cursor + 1) });
            else if (key.return) {
                if (phase.cursor === 0) {
                    // Yes: start the tunnel
                    setPhase({ ...phase, status: 'starting' });
                    void (async () => {
                        try {
                            const result = await callbacks.startN8nTunnel(phase.url);
                            setPhase({ ...phase, status: 'done', publicUrl: result.publicUrl });
                        }
                        catch (err) {
                            setPhase({ ...phase, status: 'failed', err: err.message });
                        }
                    })();
                }
                else {
                    // Skip: go to llm-proxy-setup
                    setPhase({ kind: 'llm-proxy-setup', url: phase.url, instanceProfile: phase.instanceProfile, status: 'detecting', cursor: 0 });
                }
            }
            else if (key.escape)
                cancel('Setup cancelled.');
        }
        else if (phase.kind === 'n8n-tunnel-offer' && phase.status === 'done') {
            // Auto-advance to llm-proxy-setup without showing a confirmation screen
            setPhase({ kind: 'llm-proxy-setup', url: phase.url, instanceProfile: phase.instanceProfile, status: 'detecting', cursor: 0 });
        }
        else if (phase.kind === 'n8n-tunnel-offer' && phase.status === 'failed') {
            if (key.return) {
                setPhase({ kind: 'llm-proxy-setup', url: phase.url, instanceProfile: phase.instanceProfile, status: 'detecting', cursor: 0 });
            }
        }
    }, [phase, cancel, callbacks, llmDef, surfDef, n8nDef.syncFolder, app, onDone]);
    const isSelectPhase = ['n8n-mode', 'n8n-local-ready', 'n8n-reuse-apikey', 'n8n-instance-location', 'n8n-local-runtime', 'n8n-project', 'llm-provider', 'llm-oauth-reuse', 'llm-account-auth', 'llm-reuse-config', 'llm-reuse-apikey', 'llm-reasoning-effort', 'surfaces', 'telegram-reuse-token'].includes(phase.kind)
        || (phase.kind === 'n8n-docker-check' && phase.status === 'unavailable')
        || (phase.kind === 'llm-model' && phase.models.length > 0)
        || (phase.kind === 'llm-proxy-setup' && (phase.status === 'ready' || phase.status === 'failed'))
        || (phase.kind === 'n8n-tunnel-offer' && (phase.status === 'offer' || phase.status === 'done' || phase.status === 'failed'));
    useInput((input, key) => {
        if (key.ctrl && input === 'c')
            return;
        if (isSelectPhase)
            handleSelectKey(input, key);
    }, { isActive: isSelectPhase });
    return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Header, { phase: phase, mode: mode }), renderPhase()] }));
    function renderPhase() {
        switch (phase.kind) {
            case 'n8n-mode':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "How would you like to use n8n?" }), _jsx(SelectList, { options: [
                                'Yagr installs and manages a local instance',
                                'I bring my own n8n (URL + API key)',
                            ], cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => {
                                if (v.startsWith('Yagr'))
                                    return 'Recommended (requires Docker)';
                                return 'Connect to an existing instance';
                            }, maxVisibleRows: getListViewportHeight(terminalRows, 13), maxLineWidth: listLineWidth }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-docker-check':
                if (phase.status === 'checking') {
                    return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: "Checking Docker availability\u2026", frame: spinnerFrame }) }));
                }
                if (phase.status === 'available') {
                    return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: "Docker is ready. Starting installation\u2026", frame: spinnerFrame }) }));
                }
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "yellow", bold: true, children: "Docker is not available" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: "Yagr needs Docker to install and manage a local n8n instance." }), _jsx(Text, { dimColor: true, children: "Please install and start Docker Desktop, then retry." })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", gap: 0, children: [_jsx(Text, { bold: true, children: "Installation instructions:" }), _jsx(Text, { dimColor: true, children: "  Windows / macOS:" }), _jsx(Text, { dimColor: true, children: "    1. Download Docker Desktop (free) from" }), _jsx(Text, { dimColor: true, children: "       https://www.docker.com/products/docker-desktop" }), _jsx(Text, { dimColor: true, children: "    2. Run the installer and follow the steps" }), _jsx(Text, { dimColor: true, children: "    3. Start Docker Desktop from your applications" }), _jsx(Text, { dimColor: true, children: "  Linux (Ubuntu/Debian):" }), _jsx(Text, { dimColor: true, children: "    1. Run: curl -fsSL https://get.docker.com | sh" }), _jsx(Text, { dimColor: true, children: "    2. Then: sudo usermod -aG docker $USER" }), _jsx(Text, { dimColor: true, children: "    3. Log out and back in, or run: newgrp docker" }), _jsx(Text, { dimColor: true, children: "    4. Start Docker: sudo systemctl start docker" })] }), _jsx(Box, { marginTop: 1, children: _jsx(SelectList, { options: ['Retry', 'Go back'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: 2, maxLineWidth: listLineWidth }) }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-url':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Instance URL" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleN8nUrlSubmit, placeholder: "https://your-n8n.example.com" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-reuse-apikey':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "n8n API key" }), _jsxs(Text, { dimColor: true, children: ["  A saved key exists for ", phase.url] }), _jsx(SelectList, { options: ['Keep the saved key', 'Enter a new key'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-apikey':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "n8n API key" }), _jsx(Text, { dimColor: true, children: "  Dans n8n: Settings \u2192 n8n API \u2192 Create API key." }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleN8nApiKeySubmit(phase.url), mask: "\u25CF", placeholder: "your-api-key" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-instance-location':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "S'agit-il d'une instance cloud ?" }), _jsx(Text, { dimColor: true, children: "  Choisissez cloud si votre n8n est heberge a distance et accessible publiquement." }), _jsx(SelectList, { options: ['Oui, instance cloud', 'Non, instance locale'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 12), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-local-runtime':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Savez-vous si cette instance locale n8n tourne dans Docker ?" }), _jsx(SelectList, { options: ['Oui', 'Non', 'Je ne sais pas'], cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => {
                                if (v === 'Oui')
                                    return 'Choisissez cette option si votre n8n a ete lance via Docker';
                                if (v === 'Non')
                                    return 'Choisissez cette option si votre n8n tourne directement sur votre machine';
                                return 'Par defaut, Yagr traitera ce cas comme Non';
                            }, maxVisibleRows: getListViewportHeight(terminalRows, 12), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-connecting':
                return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: `Connecting to ${phase.url}…`, frame: spinnerFrame }) }));
            case 'n8n-local-installing':
                {
                    const elapsedSeconds = Math.max(0, Math.round((Date.now() - phase.startedAt) / 1000));
                    const elapsedLabel = elapsedSeconds < 60
                        ? `${elapsedSeconds}s`
                        : `${Math.floor(elapsedSeconds / 60)}m ${String(elapsedSeconds % 60).padStart(2, '0')}s`;
                    return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SpinnerDisplay, { message: "Installing and starting a Yagr-managed local n8n instance\u2026", frame: spinnerFrame }), _jsx(Text, { dimColor: true, children: "  Yagr is waiting for the n8n API and editor to become ready before continuing." }), _jsx(Text, { dimColor: true, children: "  First run can take 1 to 3 minutes depending on Docker download and machine speed." }), _jsxs(Text, { dimColor: true, children: ["  Elapsed: ", elapsedLabel] })] }));
                }
            case 'n8n-local-ready':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Local n8n is ready" }), _jsxs(Text, { dimColor: true, children: ["  Instance URL: ", phase.url] }), _jsx(Text, { dimColor: true, children: "  Yagr waited for the n8n editor to finish starting before showing this step." }), _jsx(Text, { dimColor: true, children: "  Yagr attempted a silent owner/API bootstrap first." }), phase.note ? _jsxs(Text, { dimColor: true, children: ["  ", phase.note] }) : null, _jsx(Text, { dimColor: true, children: "  You will first create the owner account, then create an API key for Yagr." }), _jsx(SelectList, { options: ['Open n8n in the browser', 'I will open it myself'], cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => v.startsWith('Open') ? 'recommended' : phase.url, maxVisibleRows: getListViewportHeight(terminalRows, 12), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-local-auth':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Local n8n API key" }), _jsxs(Text, { dimColor: true, children: ["  Local instance URL: ", phase.url] }), _jsxs(Text, { dimColor: true, children: ["  ", phase.message] }), _jsx(Text, { dimColor: true, children: "  If the browser is not already open, run `yagr n8n local open` or open the URL manually." }), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleN8nApiKeySubmit(phase.url), mask: "\u25CF", placeholder: "Paste the new n8n API key" }) }), _jsx(HintBar, { hints: ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-project':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "n8n project" }), _jsxs(Text, { dimColor: true, children: ["  ", phase.projects.length, " project(s) found"] }), _jsx(SelectList, { options: phase.projects, cursor: phase.cursor, getLabel: (p) => p.name ?? p.id, getHint: (p) => p.id, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  select', 'Ctrl+C  cancel'] })] }));
            case 'n8n-syncfolder':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Local workflow sync folder" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleSyncFolderSubmit(phase.url, phase.apiKey, phase.project, phase.instanceProfile), placeholder: "workflows" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'n8n-saving':
                return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: "Saving n8n configuration and refreshing workspace\u2026", frame: spinnerFrame }) }));
            case 'n8n-tunnel-offer': {
                const isStarting = phase.status === 'starting';
                const isDone = phase.status === 'done';
                const isFailed = phase.status === 'failed';
                return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(FieldLabel, { label: "Setup a public URL (optional)" }), isStarting
                            ? _jsx(SpinnerDisplay, { message: "Starting Cloudflare Tunnel\u2026", frame: spinnerFrame })
                            : isDone
                                ? _jsxs(_Fragment, { children: [_jsxs(Text, { color: "green", children: ["  Tunnel ready: ", _jsx(Text, { bold: true, children: phase.publicUrl })] }), _jsx(Text, { dimColor: true, children: "  The tunnel will restart automatically on next `yagr start`." }), _jsx(SelectList, { options: ['Continue'], cursor: 0, getLabel: (v) => v, maxVisibleRows: 2, maxLineWidth: listLineWidth })] })
                                : isFailed
                                    ? _jsxs(_Fragment, { children: [_jsxs(Text, { color: "yellow", children: ["  Tunnel setup failed: ", phase.err] }), _jsx(Text, { dimColor: true, children: "  You can run it later with `yagr n8n tunnel setup`." }), _jsx(SelectList, { options: ['Continue'], cursor: 0, getLabel: (v) => v, maxVisibleRows: 2, maxLineWidth: listLineWidth })] })
                                    : _jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: "  Your local n8n needs a public URL for webhooks and Telegram triggers." }), _jsx(Text, { dimColor: true, children: "  Yagr can start a Cloudflare Tunnel to expose it." }), _jsx(SelectList, { options: ['Yes, setup a public URL', 'Skip'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: 3, maxLineWidth: listLineWidth })] }), !isStarting && _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            }
            case 'llm-proxy-setup': {
                const isDetecting = phase.status === 'detecting';
                const isProvisioning = phase.status === 'provisioning';
                const modeLabel = { local: 'local loopback', docker: 'Docker host bridge', tunnel: 'Cloudflare Tunnel' };
                return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(FieldLabel, { label: "Yagr LLM Proxy (optional)" }), isDetecting
                            ? _jsx(SpinnerDisplay, { message: "Preparing Yagr LLM Proxy\u2026", frame: spinnerFrame })
                            : isProvisioning
                                ? _jsxs(_Fragment, { children: [_jsx(SpinnerDisplay, { message: "Enabling Yagr LLM Proxy and provisioning the n8n credential\u2026", frame: spinnerFrame }), _jsx(Text, { dimColor: true, children: "  Yagr is creating or refreshing the credential used by n8n Chat Model nodes." })] })
                                : phase.status === 'failed'
                                    ? _jsxs(_Fragment, { children: [_jsxs(Text, { color: "yellow", children: ["  Relay setup failed: ", phase.err] }), _jsx(SelectList, { options: ['Skip'], cursor: 0, getLabel: (v) => v, maxVisibleRows: 2, maxLineWidth: listLineWidth })] })
                                    : _jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: "  Yagr can run a local OpenAI-compatible relay so n8n Chat Model nodes use" }), _jsx(Text, { dimColor: true, children: "  your configured provider without requiring a separate API key in n8n." }), _jsxs(Text, { dimColor: true, children: ["  Mode: ", _jsx(Text, { bold: true, children: phase.mode ? modeLabel[phase.mode] : '?' }), "  \u00B7  URL: ", _jsx(Text, { bold: true, children: phase.relayUrl })] }), _jsx(SelectList, { options: ['Enable Yagr LLM Proxy', 'Skip'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: 3, maxLineWidth: listLineWidth })] }), !isDetecting && !isProvisioning && _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            }
            case 'llm-reuse-config':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "LLM configuration" }), _jsxs(Text, { dimColor: true, children: ["  Currently configured: ", getProviderDisplayName(phase.provider), " / ", phase.model] }), _jsx(SelectList, { options: ['Keep current configuration', 'Change provider or model'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'llm-provider':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Default LLM provider" }), _jsx(Text, { color: "yellow", children: "  Warning: select only tool-capable models for Yagr agent runs." }), _jsx(SelectList, { options: VALID_PROVIDERS, cursor: phase.cursor, getLabel: (v) => getProviderDisplayName(v), getHint: (v) => {
                                const parts = [
                                    getProviderSetupHint(v),
                                    isExperimentalProvider(v) ? 'experimental' : undefined,
                                    v === phase.initial ? 'currently configured' : undefined,
                                ].filter(Boolean);
                                return parts.length > 0 ? parts.join(' · ') : undefined;
                            }, maxVisibleRows: getListViewportHeight(terminalRows, 10), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  select', 'Ctrl+C  cancel'] })] }));
            case 'llm-account-auth':
                {
                    const authCopy = getProviderAuthCopy(phase.provider);
                    const authOptions = [authCopy.continueLabel, 'Back to providers'];
                    return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: authCopy.title }), authCopy.body.map((line) => (_jsxs(Text, { dimColor: true, children: ["  ", line] }, line))), _jsx(SelectList, { options: authOptions, cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => {
                                    return (v.startsWith('Continue') || v.startsWith('Paste')) ? 'recommended' : undefined;
                                }, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
                }
            case 'llm-oauth-reuse':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${getProviderDisplayName(phase.provider)} authentication` }), _jsxs(Text, { dimColor: true, children: ["  A saved session exists for ", getProviderDisplayName(phase.provider)] }), _jsx(SelectList, { options: ['Use existing authentication', 'Renew authentication'], cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => v.startsWith('Use') ? 'recommended' : undefined, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'llm-account-input':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: phase.title }), phase.instructions.map((line, index) => (_jsx(WizardInstructionLine, { line: line }, `${index}-${line}`))), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleAccountAuthSubmit(phase.provider, phase.state), placeholder: phase.placeholder }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: [`${phase.submitLabel}  (Enter ↵)`, 'Ctrl+C  cancel'] })] }));
            case 'llm-reuse-apikey':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${getProviderDisplayName(phase.provider)} API key` }), _jsxs(Text, { dimColor: true, children: ["  A saved key exists for ", getProviderDisplayName(phase.provider)] }), _jsx(SelectList, { options: ['Keep the saved key', 'Enter a new key'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'llm-apikey':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${getProviderDisplayName(phase.provider)} API key` }), !providerRequiresApiKey(phase.provider) ? (_jsx(Text, { dimColor: true, children: "  Optional for local proxy providers. Leave empty if your proxy handles account auth." })) : null, _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: (nextValue) => {
                                    setTextValue(nextValue);
                                    llmApiKeyDraftsRef.current[phase.provider] = nextValue;
                                }, onSubmit: handleLlmApiKeySubmit(phase.provider), mask: "\u25CF", placeholder: providerRequiresApiKey(phase.provider) ? 'your-api-key' : 'optional' }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'llm-models-loading':
                const loadingMessage = isOAuthAccountProvider(phase.provider)
                    ? `Finalizing ${getProviderDisplayName(phase.provider)} account and fetching models…`
                    : `Fetching available models for ${getProviderDisplayName(phase.provider)}…`;
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SpinnerDisplay, { message: loadingMessage, frame: spinnerFrame }), phase.note ? _jsxs(Text, { dimColor: true, children: ["  ", phase.note] }) : null] }));
            case 'llm-model': {
                const modelOptions = getDisplayedModelOptions(phase.models);
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `Default model  ·  ${getProviderDisplayName(phase.provider)}` }), _jsx(Text, { color: "yellow", children: "  Warning: select only tool-capable models." }), phase.note ? _jsxs(Text, { dimColor: true, children: ["  ", phase.note] }) : null, phase.models.length === 0 ? (_jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: (v) => {
                                    const m = v.trim() || phase.defModel || '';
                                    if (!m)
                                        return;
                                    if (phase.provider === 'openai-oauth') {
                                        saveLlmAndContinue(phase.provider, phase.apiKey, m, phase.note);
                                        return;
                                    }
                                    const draftedBaseUrl = llmBaseUrlDraftsRef.current[phase.provider];
                                    const defaultBaseUrl = draftedBaseUrl ?? llmDef.getBaseUrl(phase.provider);
                                    const needsUrl = llmDef.needsBaseUrl(phase.provider);
                                    if (draftedBaseUrl) {
                                        saveLlmAndContinue(phase.provider, phase.apiKey, m, phase.note);
                                    }
                                    else if (needsUrl || defaultBaseUrl) {
                                        setPhase({ kind: 'llm-baseurl', provider: phase.provider, apiKey: phase.apiKey, model: m, def: defaultBaseUrl ?? '' });
                                        setTextValue(defaultBaseUrl ?? '');
                                    }
                                    else {
                                        saveLlmAndContinue(phase.provider, phase.apiKey, m, phase.note);
                                    }
                                }, placeholder: phase.defModel }) })) : (_jsx(SelectList, { options: modelOptions, cursor: phase.cursor, getLabel: (v) => v === '__custom__' ? '⌨  enter manually…' : v, getHint: (v) => (phase.defModel && v === phase.defModel) ? 'previously selected' : undefined, maxVisibleRows: getListViewportHeight(terminalRows, 10), maxLineWidth: listLineWidth })), _jsx(HintBar, { hints: phase.models.length > 0
                                ? ['↑↓  move', 'Enter ↵  select', 'Ctrl+C  cancel']
                                : ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            }
            case 'llm-reasoning-effort': {
                const effortOptions = ['low', 'medium', 'high'];
                const effortDescriptions = {
                    low: '~10-20% of token budget for reasoning (faster)',
                    medium: '~50% of token budget for reasoning (balanced)',
                    high: '~80-95% of token budget for reasoning (thorough)',
                };
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Reasoning effort  \u00B7  Codex" }), _jsx(Text, { dimColor: true, children: "  How much thinking budget should Codex use?" }), _jsx(SelectList, { options: effortOptions, cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => effortDescriptions[v] ?? '', maxVisibleRows: getListViewportHeight(terminalRows, 10), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            }
            case 'llm-baseurl':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${getProviderDisplayName(phase.provider)} base URL` }), _jsx(Text, { dimColor: true, children: "  Leave empty to use the default endpoint" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleBaseUrlSubmit(phase.provider, phase.apiKey, phase.model, phase.reasoningEffort), placeholder: phase.def || 'https://api.example.com/v1' }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵  confirm  (empty = default)', 'Ctrl+C  cancel'] })] }));
            case 'surfaces':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Optional messaging gateways" }), _jsx(Text, { dimColor: true, children: "  These enable Yagr to receive work from external channels" }), _jsx(Box, { marginTop: 1, children: _jsx(MultiSelectList, { options: SURFACE_OPTIONS, cursor: phase.cursor, selected: phase.selected, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }) }), _jsx(HintBar, { hints: ['↑↓  move', 'Space  toggle', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'telegram-reuse-token':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Telegram bot token" }), _jsx(Text, { dimColor: true, children: "  A saved token is already configured" }), _jsx(SelectList, { options: ['Keep the saved token', 'Enter a new token'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓  move', 'Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'telegram-token':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Telegram bot token" }), _jsx(Text, { dimColor: true, children: "  Open Telegram, search for @BotFather, then start a chat." }), _jsx(Text, { dimColor: true, children: "  Run /newbot, choose a bot name, then choose a unique username ending in \"bot\"." }), _jsx(Text, { dimColor: true, children: "  BotFather will reply with an HTTP API token like \"123456789:ABCdef...\". Paste that token here." }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleTelegramTokenSubmit(phase.surfaces), mask: "\u25CF", placeholder: "123456789:ABCdef..." }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵  confirm', 'Ctrl+C  cancel'] })] }));
            case 'telegram-connecting':
                return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: "Verifying Telegram bot token\u2026", frame: spinnerFrame }) }));
            case 'done':
                return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "green", bold: true, children: [CHECK, "  Setup complete"] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [phase.n8nHost ? _jsxs(Text, { dimColor: true, children: ["  n8n    ", DOT, "  ", phase.n8nHost] }) : null, phase.provider ? _jsxs(Text, { dimColor: true, children: ["  LLM    ", DOT, "  ", getProviderDisplayName(phase.provider), phase.model ? ` / ${phase.model}` : ''] }) : null, phase.surfaces.length > 0
                                    ? _jsxs(Text, { dimColor: true, children: ["  Gates  ", DOT, "  ", phase.surfaces.join(', ')] })
                                    : _jsxs(Text, { dimColor: true, children: ["  Gates  ", DOT, "  none"] })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Next: " }), _jsx(Text, { color: "cyan", children: "yagr start" })] })] }));
            case 'cancelled':
                return (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: "  Setup cancelled." }) }));
            case 'error':
                return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["  \u2715 ", phase.message] }) }));
        }
    }
}
//# sourceMappingURL=setup-wizard.js.map