Show configured context sources in setup

This commit is contained in:
Andrey Avtomonov 2026-05-13 15:50:24 +02:00
parent b494af3574
commit 739a193bab
2 changed files with 51 additions and 2 deletions

View file

@ -624,6 +624,32 @@ describe('setup sources step', () => {
expect(options).toContainEqual({ value: 'notion', label: 'Notion' });
});
it('shows already configured context sources in the interactive checklist', async () => {
await addPrimarySource();
await addConnection('notion-main', {
driver: 'notion',
auth_token_ref: 'env:NOTION_TOKEN',
crawl_mode: 'all_accessible',
});
const io = makeIo();
const testPrompts = prompts({ multiselect: [['back']] });
await expect(
runKtxSetupSourcesStep(
{ projectDir, inputMode: 'auto', runInitialSourceIngest: false, skipSources: false },
io.io,
{ prompts: testPrompts },
),
).resolves.toEqual({ status: 'back', projectDir });
expect(testPrompts.multiselect).toHaveBeenCalledWith(
expect.objectContaining({
initialValues: ['notion'],
options: expect.arrayContaining([{ value: 'notion', label: 'Notion', hint: 'configured: notion-main' }]),
}),
);
});
it('uses a source-specific editable connection name for new interactive connections', async () => {
await addPrimarySource();
const validateDbt = vi.fn(async () => ({ ok: true as const, detail: 'project=analytics schemas=2' }));

View file

@ -73,7 +73,8 @@ export type KtxSetupSourcesResult =
export interface KtxSetupSourcesPromptAdapter {
multiselect(options: {
message: string;
options: Array<{ value: string; label: string }>;
options: Array<{ value: string; label: string; hint?: string }>;
initialValues?: string[];
required?: boolean;
}): Promise<string[]>;
select(options: { message: string; options: Array<{ value: string; label: string }> }): Promise<string>;
@ -1325,6 +1326,22 @@ function existingConnectionIdsBySource(
.sort((left, right) => left.localeCompare(right));
}
function sourceChecklistForConnections(connections: Record<string, KtxProjectConnectionConfig>): {
options: Array<{ value: KtxSetupSourceType; label: string; hint?: string }>;
initialValues: KtxSetupSourceType[];
} {
const initialValues: KtxSetupSourceType[] = [];
const options = SOURCE_OPTIONS.map((option) => {
const existingIds = existingConnectionIdsBySource(connections, option.value);
if (existingIds.length === 0) {
return option;
}
initialValues.push(option.value);
return { ...option, hint: `configured: ${existingIds.join(', ')}` };
});
return { options, initialValues };
}
function defaultConnectionIdForSource(
connections: Record<string, KtxProjectConnectionConfig>,
source: KtxSetupSourceType,
@ -1483,13 +1500,19 @@ export async function runKtxSetupSourcesStep(
}
while (true) {
const contextSourceChecklist = sourceChecklistForConnections(
(await loadKtxProject({ projectDir: args.projectDir })).config.connections,
);
const selected = args.source
? [args.source]
: args.inputMode === 'disabled'
? []
: await prompts.multiselect({
message: withMultiselectNavigation('Which context sources should KTX ingest?'),
options: [...SOURCE_OPTIONS],
options: contextSourceChecklist.options,
...(contextSourceChecklist.initialValues.length > 0
? { initialValues: contextSourceChecklist.initialValues }
: {}),
required: false,
});
if (selected.includes('back')) {