Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/core/src/setup-helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it, vi } from 'vitest';
import { tokenSetup } from './setup-helpers.js';

describe('tokenSetup', () => {
it('collects required config fields when reusing an existing token', async () => {
const prompt = vi.fn()
.mockResolvedValueOnce(true)
.mockResolvedValueOnce('-1001234567890');
const setup = tokenSetup<{ chatId: string }>({
secretKey: 'TELEGRAM_BOT_TOKEN',
label: 'Telegram',
steps: [],
fields: [
{ key: 'chatId', message: 'Chat ID:', required: true },
],
});

const result = await setup({
secret: () => 'existing-token',
setSecret: vi.fn(),
prompt,
open: vi.fn(),
log: vi.fn(),
run: vi.fn(),
platform: process.platform,
env: process.env,
});

expect(result).toEqual({ ok: true, config: { chatId: '-1001234567890' } });
expect(prompt).toHaveBeenNthCalledWith(2, {
type: 'text',
message: 'Chat ID:',
});
});
});
26 changes: 14 additions & 12 deletions packages/core/src/setup-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,28 +157,30 @@ export interface TokenSetupOpts<C = unknown> {
export function tokenSetup<C = unknown>(opts: TokenSetupOpts<C>): SetupFn<C> {
return async (ctx) => {
const existing = ctx.secret(opts.secretKey);
let reuseExisting = false;
if (existing) {
const reuse = await ctx.prompt<boolean>({
reuseExisting = await ctx.prompt<boolean>({
type: 'confirm',
message: `${opts.secretKey} already in vault — reuse it?`,
initial: true,
});
if (reuse) return { ok: true, config: (opts.config ?? {}) as C };
}

ctx.log(`${opts.label} setup:`);
for (const line of opts.steps) ctx.log(` ${line}`);
if (opts.vendorDocUrl) await ctx.open(opts.vendorDocUrl);
if (!reuseExisting) {
ctx.log(`${opts.label} setup:`);
for (const line of opts.steps) ctx.log(` ${line}`);
if (opts.vendorDocUrl) await ctx.open(opts.vendorDocUrl);

const token = await ctx.prompt<string>({
type: 'password',
message: `Paste the ${opts.label} API token:`,
});
const token = await ctx.prompt<string>({
type: 'password',
message: `Paste the ${opts.label} API token:`,
});

if (!token) {
return { ok: false, config: (opts.config ?? {}) as C, manual: opts.steps };
if (!token) {
return { ok: false, config: (opts.config ?? {}) as C, manual: opts.steps };
}
await ctx.setSecret(opts.secretKey, token);
}
await ctx.setSecret(opts.secretKey, token);

const configExtras: Record<string, string> = {};
for (const field of opts.fields ?? []) {
Expand Down
Loading