mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat: add interactive CLI token configuration with settings persistence
Implements comprehensive token management with intelligent priority resolution. Token sources (highest to lowest priority): environment variable, settings file, interactive prompt. Changes include: - Updated home directory from ~/.happy to ~/.config/hapi (env var: HAPI_HOME_DIR) - Added cliApiToken field to Settings interface for persistent storage - Made cliApiToken mutable in Configuration class (private field with getter/setter) - Created ui/tokenInit.ts module for token initialization with fallback prompt - Enhanced auth command: login (interactive input), logout (clear token), status (show source) - Updated doctor command to correctly identify token source and mask saved token in output - Token initialization called early in startup flow to ensure availability before API use
This commit is contained in:
+15
-6
@@ -23,7 +23,7 @@ import packageJson from '../../package.json'
|
||||
export function getEnvironmentInfo(): Record<string, any> {
|
||||
return {
|
||||
PWD: process.env.PWD,
|
||||
HAPPY_HOME_DIR: process.env.HAPPY_HOME_DIR,
|
||||
HAPI_HOME_DIR: process.env.HAPI_HOME_DIR,
|
||||
HAPPY_BOT_URL: process.env.HAPPY_BOT_URL,
|
||||
HAPPY_PROJECT_ROOT: process.env.HAPPY_PROJECT_ROOT,
|
||||
CLI_API_TOKEN_SET: Boolean(process.env.CLI_API_TOKEN),
|
||||
@@ -111,7 +111,7 @@ export async function runDoctorCommand(filter?: 'all' | 'daemon'): Promise<void>
|
||||
// Environment
|
||||
console.log(chalk.bold('\n🌍 Environment Variables'));
|
||||
const env = getEnvironmentInfo();
|
||||
console.log(`HAPPY_HOME_DIR: ${env.HAPPY_HOME_DIR ? chalk.green(env.HAPPY_HOME_DIR) : chalk.gray('not set')}`);
|
||||
console.log(`HAPI_HOME_DIR: ${env.HAPI_HOME_DIR ? chalk.green(env.HAPI_HOME_DIR) : chalk.gray('not set')}`);
|
||||
console.log(`HAPPY_BOT_URL: ${env.HAPPY_BOT_URL ? chalk.green(env.HAPPY_BOT_URL) : chalk.gray('not set')}`);
|
||||
console.log(`CLI_API_TOKEN: ${env.CLI_API_TOKEN_SET ? chalk.green('set') : chalk.gray('not set')}`);
|
||||
console.log(`DANGEROUSLY_LOG_TO_SERVER: ${env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING ? chalk.yellow('ENABLED') : chalk.gray('not set')}`);
|
||||
@@ -119,21 +119,30 @@ export async function runDoctorCommand(filter?: 'all' | 'daemon'): Promise<void>
|
||||
console.log(`NODE_ENV: ${env.NODE_ENV ? chalk.green(env.NODE_ENV) : chalk.gray('not set')}`);
|
||||
|
||||
// Settings
|
||||
let settings;
|
||||
try {
|
||||
const settings = await readSettings();
|
||||
settings = await readSettings();
|
||||
console.log(chalk.bold('\n📄 Settings (settings.json):'));
|
||||
console.log(chalk.gray(JSON.stringify(settings, null, 2)));
|
||||
// Hide cliApiToken in output for security
|
||||
const displaySettings = { ...settings, cliApiToken: settings.cliApiToken ? '***' : undefined };
|
||||
console.log(chalk.gray(JSON.stringify(displaySettings, null, 2)));
|
||||
} catch (error) {
|
||||
console.log(chalk.bold('\n📄 Settings:'));
|
||||
console.log(chalk.red('❌ Failed to read settings'));
|
||||
settings = {};
|
||||
}
|
||||
|
||||
// Authentication status (direct-connect)
|
||||
console.log(chalk.bold('\n🔐 Direct Connect Auth'));
|
||||
if (configuration.cliApiToken) {
|
||||
console.log(chalk.green('✓ CLI_API_TOKEN is set'));
|
||||
const envToken = process.env.CLI_API_TOKEN;
|
||||
const settingsToken = settings.cliApiToken;
|
||||
const hasToken = Boolean(envToken || settingsToken);
|
||||
const tokenSource = envToken ? 'environment variable' : (settingsToken ? 'settings file' : 'none');
|
||||
if (hasToken) {
|
||||
console.log(chalk.green(`✓ CLI_API_TOKEN is set (from ${tokenSource})`));
|
||||
} else {
|
||||
console.log(chalk.red('❌ CLI_API_TOKEN is not set'));
|
||||
console.log(chalk.gray(' Run `hapi auth login` to configure or set CLI_API_TOKEN env var'));
|
||||
}
|
||||
|
||||
// Legacy credentials (unused in direct-connect mode)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Token initialization module
|
||||
*
|
||||
* Handles CLI_API_TOKEN initialization with priority:
|
||||
* 1. Environment variable (highest - allows temporary override)
|
||||
* 2. Settings file (~/.config/hapi/settings.json)
|
||||
* 3. Interactive prompt (only when both above are missing)
|
||||
*/
|
||||
|
||||
import * as readline from 'node:readline/promises'
|
||||
import { stdin as input, stdout as output } from 'node:process'
|
||||
import chalk from 'chalk'
|
||||
import { configuration } from '@/configuration'
|
||||
import { readSettings, updateSettings } from '@/persistence'
|
||||
|
||||
/**
|
||||
* Initialize CLI API token
|
||||
* Must be called before any API operations
|
||||
*/
|
||||
export async function initializeToken(): Promise<void> {
|
||||
// 1. Environment variable has highest priority (allows temporary override)
|
||||
if (configuration.cliApiToken) {
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Read from settings file
|
||||
const settings = await readSettings()
|
||||
if (settings.cliApiToken) {
|
||||
configuration._setCliApiToken(settings.cliApiToken)
|
||||
return
|
||||
}
|
||||
|
||||
// 3. Non-TTY environment cannot prompt, fail with clear error
|
||||
if (!process.stdin.isTTY) {
|
||||
throw new Error('CLI_API_TOKEN is required. Set it via environment variable or run `hapi auth login`.')
|
||||
}
|
||||
|
||||
// 4. Interactive prompt
|
||||
const token = await promptForToken()
|
||||
|
||||
// 5. Save and update configuration
|
||||
await updateSettings(current => ({
|
||||
...current,
|
||||
cliApiToken: token
|
||||
}))
|
||||
configuration._setCliApiToken(token)
|
||||
}
|
||||
|
||||
async function promptForToken(): Promise<string> {
|
||||
const rl = readline.createInterface({ input, output })
|
||||
|
||||
console.log(chalk.yellow('\nNo CLI_API_TOKEN found.'))
|
||||
console.log(chalk.gray('You can set it via environment variable or enter it now.\n'))
|
||||
|
||||
try {
|
||||
const token = await rl.question(chalk.cyan('Enter CLI_API_TOKEN: '))
|
||||
if (!token.trim()) {
|
||||
throw new Error('Token cannot be empty')
|
||||
}
|
||||
console.log(chalk.green('\nToken saved to ~/.config/hapi/settings.json'))
|
||||
return token.trim()
|
||||
} finally {
|
||||
rl.close()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user