cli: add openai-key and anthropic token flag

This commit is contained in:
William Valentin
2026-02-15 10:29:31 -08:00
parent 6375f56f67
commit 49c8ff620f
4 changed files with 93 additions and 10 deletions
+30 -10
View File
@@ -1,6 +1,11 @@
import type { Command } from 'commander';
import readline from 'readline';
import { loadStoredAnthropicAuth, storeAnthropicAuth } from '../auth/index.js';
import {
loadStoredAnthropicAuth,
loadStoredAnthropicAuthToken,
storeAnthropicAuth,
storeAnthropicAuthToken,
} from '../auth/index.js';
async function promptHidden(question: string): Promise<string> {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
@@ -29,13 +34,23 @@ async function promptHidden(question: string): Promise<string> {
export function registerAnthropicAuthCommand(program: Command): void {
program
.command('anthropic-auth')
.description('Store an Anthropic API key (auth.json)')
.action(async () => {
const existing = loadStoredAnthropicAuth();
if (existing) {
console.log('Anthropic credential already exists.');
console.log('Delete ~/.config/flynn/auth.json anthropic entry if you want to re-authenticate.');
process.exit(0);
.description('Store an Anthropic API key or auth token (auth.json)')
.option('--token', 'Store an Anthropic auth token instead of an API key')
.action(async (opts: { token?: boolean }) => {
if (opts.token) {
if (loadStoredAnthropicAuthToken()) {
console.log('Anthropic auth token already exists.');
console.log('Delete ~/.config/flynn/auth.json anthropic.auth_token entry if you want to re-authenticate.');
process.exit(0);
}
} else {
const existing = loadStoredAnthropicAuth();
if (existing?.api_key) {
console.log('Anthropic API key already exists.');
console.log('Delete ~/.config/flynn/auth.json anthropic.api_key entry if you want to re-authenticate.');
process.exit(0);
}
}
console.log('Anthropic uses API keys for authentication.');
@@ -43,8 +58,13 @@ export function registerAnthropicAuthCommand(program: Command): void {
console.log('');
try {
const apiKey = await promptHidden('Enter Anthropic API key: ');
storeAnthropicAuth(apiKey);
if (opts.token) {
const token = await promptHidden('Enter Anthropic auth token: ');
storeAnthropicAuthToken(token);
} else {
const apiKey = await promptHidden('Enter Anthropic API key: ');
storeAnthropicAuth(apiKey);
}
console.log('');
console.log('Anthropic credential stored in ~/.config/flynn/auth.json');
} catch (error) {
+5
View File
@@ -13,6 +13,11 @@ describe('CLI program', () => {
expect(commandNames).toContain('doctor');
expect(commandNames).toContain('config');
expect(commandNames).toContain('skills');
expect(commandNames).toContain('openai-auth');
expect(commandNames).toContain('openai-key');
expect(commandNames).toContain('anthropic-auth');
expect(commandNames).toContain('zai-auth');
});
it('has version info', () => {
+2
View File
@@ -19,6 +19,7 @@ import { registerGdocsAuthCommand } from './gdocs-auth.js';
import { registerGdriveAuthCommand } from './gdrive-auth.js';
import { registerGtasksAuthCommand } from './gtasks-auth.js';
import { registerOpenaiAuthCommand } from './openai-auth.js';
import { registerOpenaiKeyCommand } from './openai-key.js';
import { registerZaiAuthCommand } from './zai-auth.js';
import { registerAnthropicAuthCommand } from './anthropic-auth.js';
import { registerSkillsCommand } from './skills.js';
@@ -45,6 +46,7 @@ export function createProgram(): Command {
registerGdriveAuthCommand(program);
registerGtasksAuthCommand(program);
registerOpenaiAuthCommand(program);
registerOpenaiKeyCommand(program);
registerZaiAuthCommand(program);
registerAnthropicAuthCommand(program);
registerSkillsCommand(program);
+56
View File
@@ -0,0 +1,56 @@
import type { Command } from 'commander';
import readline from 'readline';
import { loadStoredOpenAIApiKey, storeOpenAIApiKey } from '../auth/index.js';
async function promptHidden(question: string): Promise<string> {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
const rlAny = rl as unknown as { stdoutMuted?: boolean; _writeToOutput?: (s: string) => void };
rlAny.stdoutMuted = true;
rlAny._writeToOutput = (s: string) => {
if (!rlAny.stdoutMuted) {
process.stdout.write(s);
return;
}
if (s.includes('\n')) {
process.stdout.write('\n');
} else {
process.stdout.write('*');
}
};
const answer = await new Promise<string>((resolve) => rl.question(question, resolve));
rlAny.stdoutMuted = false;
rl.close();
process.stdout.write('\n');
return answer.trim();
}
export function registerOpenaiKeyCommand(program: Command): void {
program
.command('openai-key')
.description('Store an OpenAI API key (auth.json)')
.action(async () => {
const existing = loadStoredOpenAIApiKey();
if (existing) {
console.log('OpenAI API key already exists.');
console.log('Delete ~/.config/flynn/auth.json openai.api_key entry if you want to re-authenticate.');
process.exit(0);
}
console.log('OpenAI uses API keys for standard API access.');
console.log('Create a key at: https://platform.openai.com/api-keys');
console.log('');
try {
const apiKey = await promptHidden('Enter OpenAI API key: ');
storeOpenAIApiKey(apiKey);
console.log('');
console.log('OpenAI API key stored in ~/.config/flynn/auth.json');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`OpenAI API key storage failed: ${message}`);
process.exit(1);
}
});
}