refactor: retire old entry points, delegate to CLI
This commit is contained in:
+2
-36
@@ -1,36 +1,2 @@
|
|||||||
import { loadConfig } from './config/index.js';
|
// Legacy entry point — delegates to CLI
|
||||||
import { startDaemon } from './daemon/index.js';
|
import './cli/index.js';
|
||||||
import { resolve } from 'path';
|
|
||||||
import { homedir } from 'os';
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
|
|
||||||
const CONFIG_PATH = process.env.FLYNN_CONFIG
|
|
||||||
?? resolve(homedir(), '.config/flynn/config.yaml');
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
console.log('Flynn starting...');
|
|
||||||
console.log(`Loading config from: ${CONFIG_PATH}`);
|
|
||||||
|
|
||||||
if (!existsSync(CONFIG_PATH)) {
|
|
||||||
console.error(`Config file not found: ${CONFIG_PATH}`);
|
|
||||||
console.error('Copy config/default.yaml to ~/.config/flynn/config.yaml and configure it.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const config = loadConfig(CONFIG_PATH);
|
|
||||||
const daemon = await startDaemon(config);
|
|
||||||
|
|
||||||
console.log(`Allowed Telegram chat IDs: ${config.telegram.allowed_chat_ids.join(', ')}`);
|
|
||||||
|
|
||||||
// Keep process alive
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
daemon.lifecycle.onShutdown(async () => resolve());
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to start Flynn:', error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
|||||||
+6
-226
@@ -1,227 +1,7 @@
|
|||||||
import { loadConfig } from './config/index.js';
|
// Legacy entry point — delegates to CLI
|
||||||
import { SessionStore, SessionManager } from './session/index.js';
|
// When invoked directly, behaves like 'flynn tui'
|
||||||
import { AnthropicClient, OpenAIClient, OllamaClient, LlamaCppClient, ModelRouter } from './models/index.js';
|
import { createProgram } from './cli/index.js';
|
||||||
import { MinimalTui, startFullscreenTui } from './frontends/tui/index.js';
|
|
||||||
import { NativeAgent } from './backends/index.js';
|
|
||||||
import { ToolRegistry, ToolExecutor, allBuiltinTools } from './tools/index.js';
|
|
||||||
import { HookEngine } from './hooks/index.js';
|
|
||||||
import type { Config } from './config/index.js';
|
|
||||||
import { resolve } from 'path';
|
|
||||||
import { homedir } from 'os';
|
|
||||||
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
|
||||||
|
|
||||||
const CONFIG_PATH = process.env.FLYNN_CONFIG
|
const program = createProgram();
|
||||||
?? resolve(homedir(), '.config/flynn/config.yaml');
|
const args = ['node', 'flynn', 'tui', ...process.argv.slice(2)];
|
||||||
|
program.parse(args);
|
||||||
// ANSI color codes for tool status display
|
|
||||||
const toolColors = {
|
|
||||||
reset: '\x1b[0m',
|
|
||||||
dim: '\x1b[2m',
|
|
||||||
cyan: '\x1b[36m',
|
|
||||||
green: '\x1b[32m',
|
|
||||||
red: '\x1b[31m',
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadSystemPrompt(): string {
|
|
||||||
const paths = [
|
|
||||||
resolve(process.cwd(), 'SOUL.md'),
|
|
||||||
resolve(import.meta.dirname, '../SOUL.md'),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const soulPath of paths) {
|
|
||||||
if (existsSync(soulPath)) {
|
|
||||||
return readFileSync(soulPath, 'utf-8');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'You are Flynn, a helpful personal AI assistant. Be direct, concise, and helpful. Use markdown when it improves readability.';
|
|
||||||
}
|
|
||||||
|
|
||||||
function createModelRouter(config: Config): ModelRouter {
|
|
||||||
const models = config.models;
|
|
||||||
|
|
||||||
const defaultClient = new AnthropicClient({
|
|
||||||
model: models.default.model,
|
|
||||||
apiKey: models.default.api_key,
|
|
||||||
authToken: models.default.auth_token,
|
|
||||||
});
|
|
||||||
|
|
||||||
let fastClient;
|
|
||||||
let complexClient;
|
|
||||||
let localClient;
|
|
||||||
|
|
||||||
if (models.fast) {
|
|
||||||
fastClient = new AnthropicClient({
|
|
||||||
model: models.fast.model,
|
|
||||||
apiKey: models.fast.api_key,
|
|
||||||
authToken: models.fast.auth_token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (models.complex) {
|
|
||||||
complexClient = new AnthropicClient({
|
|
||||||
model: models.complex.model,
|
|
||||||
apiKey: models.complex.api_key,
|
|
||||||
authToken: models.complex.auth_token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (models.local) {
|
|
||||||
if (models.local.provider === 'ollama') {
|
|
||||||
localClient = new OllamaClient({
|
|
||||||
model: models.local.model,
|
|
||||||
host: models.local.endpoint,
|
|
||||||
numGpu: models.local.num_gpu,
|
|
||||||
});
|
|
||||||
} else if (models.local.provider === 'llamacpp') {
|
|
||||||
localClient = new LlamaCppClient({
|
|
||||||
endpoint: models.local.endpoint ?? 'http://localhost:8080',
|
|
||||||
model: models.local.model,
|
|
||||||
authToken: models.local.auth_token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fallbackChain = [];
|
|
||||||
for (const providerName of models.fallback_chain) {
|
|
||||||
if (providerName === 'openai') {
|
|
||||||
fallbackChain.push(new OpenAIClient({ model: 'gpt-4o' }));
|
|
||||||
} else if (providerName === 'local' && localClient) {
|
|
||||||
fallbackChain.push(localClient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ModelRouter({
|
|
||||||
default: defaultClient,
|
|
||||||
fast: fastClient,
|
|
||||||
complex: complexClient,
|
|
||||||
local: localClient,
|
|
||||||
fallbackChain,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
const fullscreenMode = args.includes('--fullscreen') || args.includes('-f');
|
|
||||||
|
|
||||||
console.log('Flynn TUI starting...');
|
|
||||||
|
|
||||||
if (!existsSync(CONFIG_PATH)) {
|
|
||||||
console.error(`Config file not found: ${CONFIG_PATH}`);
|
|
||||||
console.error('Copy config/default.yaml to ~/.config/flynn/config.yaml and configure it.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = loadConfig(CONFIG_PATH);
|
|
||||||
|
|
||||||
// Ensure data directory exists
|
|
||||||
const dataDir = resolve(homedir(), '.local/share/flynn');
|
|
||||||
mkdirSync(dataDir, { recursive: true });
|
|
||||||
|
|
||||||
// Initialize components
|
|
||||||
const sessionStore = new SessionStore(resolve(dataDir, 'sessions.db'));
|
|
||||||
const sessionManager = new SessionManager(sessionStore);
|
|
||||||
const modelRouter = createModelRouter(config);
|
|
||||||
const systemPrompt = loadSystemPrompt();
|
|
||||||
|
|
||||||
// Initialize tool registry and executor
|
|
||||||
const hookEngine = new HookEngine(config.hooks);
|
|
||||||
const toolRegistry = new ToolRegistry();
|
|
||||||
for (const tool of allBuiltinTools) {
|
|
||||||
toolRegistry.register(tool);
|
|
||||||
}
|
|
||||||
const toolExecutor = new ToolExecutor(toolRegistry, hookEngine);
|
|
||||||
|
|
||||||
// Get TUI session
|
|
||||||
const session = sessionManager.getSession('tui', 'local');
|
|
||||||
|
|
||||||
// Create agent with tools and tool status display
|
|
||||||
const agent = new NativeAgent({
|
|
||||||
modelClient: modelRouter,
|
|
||||||
systemPrompt,
|
|
||||||
session,
|
|
||||||
toolRegistry,
|
|
||||||
toolExecutor,
|
|
||||||
onToolUse: (event) => {
|
|
||||||
if (event.type === 'start') {
|
|
||||||
const argsStr = event.args ? ` ${toolColors.dim}${JSON.stringify(event.args)}${toolColors.reset}` : '';
|
|
||||||
process.stdout.write(`${toolColors.cyan}> ${event.tool}${toolColors.reset}${argsStr}\n`);
|
|
||||||
} else if (event.type === 'end' && event.result) {
|
|
||||||
const icon = event.result.success ? `${toolColors.green}done` : `${toolColors.red}error`;
|
|
||||||
const detail = event.result.success
|
|
||||||
? `${toolColors.dim}(${event.result.output.split('\n').length} lines)${toolColors.reset}`
|
|
||||||
: `${toolColors.dim}${event.result.error ?? 'unknown error'}${toolColors.reset}`;
|
|
||||||
process.stdout.write(` ${icon}${toolColors.reset} ${detail}\n`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const cleanup = () => {
|
|
||||||
sessionStore.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
|
||||||
cleanup();
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fullscreenMode) {
|
|
||||||
// Start fullscreen Ink UI
|
|
||||||
await startFullscreenTui({
|
|
||||||
session,
|
|
||||||
modelClient: modelRouter,
|
|
||||||
modelRouter,
|
|
||||||
systemPrompt: systemPrompt,
|
|
||||||
model: config.models.default.model,
|
|
||||||
onExit: cleanup,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Start minimal readline UI
|
|
||||||
let switchingToFullscreen = false;
|
|
||||||
|
|
||||||
const tui = new MinimalTui({
|
|
||||||
session,
|
|
||||||
modelClient: modelRouter,
|
|
||||||
modelRouter,
|
|
||||||
systemPrompt,
|
|
||||||
agent,
|
|
||||||
localProviders: config.models.local_providers,
|
|
||||||
currentLocalProvider: config.models.local?.provider,
|
|
||||||
onTransfer: (target) => {
|
|
||||||
if (target === 'telegram') {
|
|
||||||
const telegramUserId = String(config.telegram.allowed_chat_ids[0]);
|
|
||||||
sessionManager.transferSession('tui', 'local', 'telegram', telegramUserId);
|
|
||||||
console.log(`Session transferred to Telegram (${telegramUserId})\n`);
|
|
||||||
} else {
|
|
||||||
console.log(`Unknown transfer target: ${target}\n`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onFullscreen: () => {
|
|
||||||
switchingToFullscreen = true;
|
|
||||||
tui.stop(true); // Preserve stdin for fullscreen mode
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await tui.start();
|
|
||||||
|
|
||||||
if (switchingToFullscreen) {
|
|
||||||
console.clear();
|
|
||||||
await startFullscreenTui({
|
|
||||||
session,
|
|
||||||
modelClient: modelRouter,
|
|
||||||
modelRouter,
|
|
||||||
systemPrompt,
|
|
||||||
model: config.models.default.model,
|
|
||||||
onExit: cleanup,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch((error) => {
|
|
||||||
console.error('Failed to start TUI:', error);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user