160 lines
5.6 KiB
TypeScript
160 lines
5.6 KiB
TypeScript
import * as vscode from 'vscode';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
// axios removed in favor of native fetch
|
|
import {
|
|
_getBrainDir,
|
|
_isBrainDirExplicitlySet,
|
|
findBrainFiles,
|
|
SYSTEM_PROMPT,
|
|
buildApiUrl,
|
|
logError,
|
|
logInfo
|
|
} from './utils';
|
|
import { getConfig, validateConfig } from './config';
|
|
import { AgentExecutor } from './agent';
|
|
import { BridgeServer } from './bridge';
|
|
import { SidebarChatProvider } from './sidebarProvider';
|
|
import { HealthCheckMonitor } from './core/health';
|
|
import { initAstraPathResolver } from './core/astraPath';
|
|
|
|
/**
|
|
* Astra Extension Entry Point
|
|
*/
|
|
export async function activate(context: vscode.ExtensionContext) {
|
|
logInfo('Astra activating...');
|
|
|
|
// Initialize Astra Path Resolver (.astra → ConnectAI/.astra/)
|
|
initAstraPathResolver(context);
|
|
|
|
// Start Environment Health Monitoring
|
|
HealthCheckMonitor.runAllChecks();
|
|
HealthCheckMonitor.startInterval(600000); // Check every 10 mins
|
|
|
|
// 0. Validate Configuration
|
|
const validation = validateConfig();
|
|
if (!validation.valid) {
|
|
vscode.window.showErrorMessage(`Astra Configuration Error: ${validation.errors.join(' ')}`);
|
|
logError('Configuration validation failed.', { errors: validation.errors });
|
|
}
|
|
|
|
// 1. Ensure Brain Directory
|
|
await _ensureBrainDir(context);
|
|
|
|
// 2. Initialize Agent Executor
|
|
const agent = new AgentExecutor(context);
|
|
|
|
// 3. Initialize Sidebar Provider
|
|
const provider = new SidebarChatProvider(context.extensionUri, context, agent);
|
|
context.subscriptions.push(
|
|
vscode.window.registerWebviewViewProvider(SidebarChatProvider.viewType, provider)
|
|
);
|
|
|
|
// 4. Initialize Bridge Server (Port 4825)
|
|
const bridge = new BridgeServer(provider);
|
|
try {
|
|
bridge.start();
|
|
logInfo('Bridge server started on port 4825.');
|
|
} catch (err) {
|
|
logError('Failed to start bridge server.', err);
|
|
}
|
|
|
|
// 5. Register Core Commands
|
|
context.subscriptions.push(
|
|
vscode.commands.registerCommand('g1nation.focusInput', () => {
|
|
provider.focusInput();
|
|
})
|
|
);
|
|
|
|
context.subscriptions.push(
|
|
vscode.commands.registerCommand('g1nation.clearChat', () => {
|
|
provider.clearChat();
|
|
})
|
|
);
|
|
|
|
context.subscriptions.push(
|
|
vscode.commands.registerCommand('g1nation.syncBrain', async () => {
|
|
await provider.syncBrain();
|
|
})
|
|
);
|
|
|
|
// 6. Run Initial Setup (Automatic Model/Engine Detection)
|
|
const setupComplete = context.globalState.get<boolean>('setupComplete', false);
|
|
if (!setupComplete) {
|
|
await runInitialSetup(context);
|
|
}
|
|
}
|
|
|
|
export function deactivate() {}
|
|
|
|
async function runInitialSetup(context: vscode.ExtensionContext) {
|
|
try {
|
|
let engineName = '';
|
|
let modelName = '';
|
|
|
|
try {
|
|
const res = await fetch(buildApiUrl('http://127.0.0.1:1234', 'lmstudio', 'models'), { signal: AbortSignal.timeout(2000) });
|
|
const data = await res.json() as any;
|
|
if (data?.data?.length > 0) {
|
|
engineName = 'LM Studio';
|
|
modelName = data.data[0].id;
|
|
await vscode.workspace.getConfiguration('g1nation').update('ollamaUrl', 'http://127.0.0.1:1234', vscode.ConfigurationTarget.Global);
|
|
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', modelName, vscode.ConfigurationTarget.Global);
|
|
logInfo('Initial setup detected LM Studio.', { modelName });
|
|
}
|
|
} catch (err) {
|
|
logInfo('Initial setup could not reach LM Studio.', err);
|
|
}
|
|
|
|
if (!engineName) {
|
|
try {
|
|
const res = await fetch('http://127.0.0.1:11434/api/tags', { signal: AbortSignal.timeout(2000) });
|
|
const data = await res.json() as any;
|
|
if (data?.models?.length > 0) {
|
|
engineName = 'Ollama';
|
|
modelName = data.models[0].name;
|
|
await vscode.workspace.getConfiguration('g1nation').update('ollamaUrl', 'http://127.0.0.1:11434', vscode.ConfigurationTarget.Global);
|
|
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', modelName, vscode.ConfigurationTarget.Global);
|
|
logInfo('Initial setup detected Ollama.', { modelName });
|
|
}
|
|
} catch (err) {
|
|
logInfo('Initial setup could not reach Ollama.', err);
|
|
}
|
|
}
|
|
|
|
context.globalState.update('setupComplete', true);
|
|
if (engineName) {
|
|
vscode.window.showInformationMessage(`Setup Complete: ${engineName} detected with model ${modelName}`);
|
|
}
|
|
} catch (e) {
|
|
logError('Initial setup failed.', e);
|
|
context.globalState.update('setupComplete', true);
|
|
}
|
|
}
|
|
|
|
async function _ensureBrainDir(context: vscode.ExtensionContext): Promise<string | null> {
|
|
if (_isBrainDirExplicitlySet()) {
|
|
const dir = _getBrainDir();
|
|
if (!fs.existsSync(dir)) {
|
|
try {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
} catch (e) {}
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
const defaultDir = _getBrainDir();
|
|
if (!fs.existsSync(defaultDir)) {
|
|
try {
|
|
fs.mkdirSync(defaultDir, { recursive: true });
|
|
// Create a welcome file
|
|
fs.writeFileSync(path.join(defaultDir, 'Welcome.md'), "# Welcome to your Second Brain\n\nAstra will store and retrieve knowledge from here.");
|
|
} catch (e) {}
|
|
}
|
|
return defaultDir;
|
|
}
|
|
|
|
/**
|
|
* Astra Extension Entry Point
|
|
*/
|