termcode
Version:
Superior terminal AI coding agent with enterprise-grade security, intelligent error recovery, performance monitoring, and plugin system - Advanced Claude Code alternative
193 lines (192 loc) • 7.33 kB
JavaScript
import { promises as fs } from "node:fs";
import path from "node:path";
import os from "node:os";
import { getProvider } from "../providers/index.js";
import { log } from "../util/logging.js";
const defaultOfflineConfig = {
enabled: false,
localProvider: "ollama",
localModel: "llama3.1:8b",
allowNetworkFallback: false,
vectorStore: "sqlite",
encryptData: true,
dataRetentionDays: 30
};
const offlineDir = path.join(os.homedir(), ".termcode", "offline");
const configPath = path.join(offlineDir, "config.json");
// Initialize offline mode directory
async function ensureOfflineDir() {
await fs.mkdir(offlineDir, { recursive: true });
}
// Load offline configuration
export async function loadOfflineConfig() {
try {
await ensureOfflineDir();
const content = await fs.readFile(configPath, "utf8");
return { ...defaultOfflineConfig, ...JSON.parse(content) };
}
catch (error) {
return defaultOfflineConfig;
}
}
// Save offline configuration
export async function saveOfflineConfig(config) {
try {
await ensureOfflineDir();
const currentConfig = await loadOfflineConfig();
const updatedConfig = { ...currentConfig, ...config };
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2), "utf8");
}
catch (error) {
log.error("Failed to save offline config:", error);
throw error;
}
}
// Check if local model is available
export async function checkLocalModelAvailability(provider, model) {
try {
const providerInstance = getProvider(provider);
const models = await providerInstance.listModels();
return models.some(m => m.id === model && m.type === "chat");
}
catch (error) {
return false;
}
}
// Get current offline status
export async function getOfflineStatus() {
const config = await loadOfflineConfig();
const localModelAvailable = await checkLocalModelAvailability(config.localProvider, config.localModel);
let mode = "online";
if (config.enabled && !config.allowNetworkFallback) {
mode = "offline";
}
else if (config.enabled && config.allowNetworkFallback) {
mode = "hybrid";
}
const restrictedOperations = [];
if (config.enabled && !config.allowNetworkFallback) {
restrictedOperations.push("Cloud model usage", "GitHub API calls", "Issue tracker sync", "Package registry access", "Web search");
}
return {
mode,
localModelAvailable,
vectorStoreStatus: localModelAvailable ? "ready" : "error",
dataEncrypted: config.encryptData,
restrictedOperations
};
}
// Enable offline mode
export async function enableOfflineMode(options = {}) {
const config = await loadOfflineConfig();
const updatedConfig = {
...config,
enabled: true,
...options
};
// Verify local model availability
const modelAvailable = await checkLocalModelAvailability(updatedConfig.localProvider, updatedConfig.localModel);
if (!modelAvailable) {
throw new Error(`Local model ${updatedConfig.localModel} not available. Install with: ollama pull ${updatedConfig.localModel}`);
}
await saveOfflineConfig(updatedConfig);
log.success("Offline mode enabled");
log.info(`Using local model: ${updatedConfig.localProvider}/${updatedConfig.localModel}`);
if (!updatedConfig.allowNetworkFallback) {
log.warn("Network access disabled - some features will be unavailable");
}
}
// Disable offline mode
export async function disableOfflineMode() {
await saveOfflineConfig({ enabled: false });
log.success("Offline mode disabled - network access restored");
}
// Check if network access is allowed for operation
export function isNetworkAllowed(operation) {
// This will be checked by other modules before making network calls
const offlineConfig = global.__offlineConfig;
if (!offlineConfig?.enabled)
return true;
if (offlineConfig.allowNetworkFallback)
return true;
// In strict offline mode, no network operations allowed
return false;
}
// Network access guard for operations
export async function guardNetworkAccess(operation) {
const config = await loadOfflineConfig();
if (config.enabled && !config.allowNetworkFallback) {
throw new Error(`Operation "${operation}" requires network access but offline mode is enabled. Use --allow-network or disable offline mode.`);
}
}
// Initialize offline mode on startup
export async function initializeOfflineMode() {
const config = await loadOfflineConfig();
if (config.enabled) {
// Store config globally for quick access
global.__offlineConfig = config;
log.info(`Offline mode active: ${config.allowNetworkFallback ? "hybrid" : "strict"}`);
// Verify local model
const modelAvailable = await checkLocalModelAvailability(config.localProvider, config.localModel);
if (!modelAvailable) {
log.warn(`Local model ${config.localModel} not available - some operations may fail`);
}
}
}
// Cleanup old offline data based on retention policy
export async function cleanupOfflineData() {
const config = await loadOfflineConfig();
if (config.dataRetentionDays <= 0)
return;
const cutoffTime = Date.now() - (config.dataRetentionDays * 24 * 60 * 60 * 1000);
try {
// Clean up old vector store entries
const vectorDbPath = path.join(offlineDir, "vectors.db");
if (config.vectorStore === "sqlite") {
// This would require sqlite3 integration
log.info(`Data cleanup: retention policy ${config.dataRetentionDays} days`);
}
}
catch (error) {
log.warn("Failed to cleanup old offline data:", error);
}
}
// Get offline mode recommendations
export async function getOfflineModeRecommendations() {
const hasOllama = await checkLocalModelAvailability("ollama", "llama3.1:8b");
if (!hasOllama) {
return {
suitable: false,
reasoning: "Local model not available. Offline mode requires Ollama with downloaded models.",
requirements: [
"Install Ollama (https://ollama.ai)",
"Download model: ollama pull llama3.1:8b",
"Start Ollama service: ollama serve"
],
benefits: [],
limitations: []
};
}
return {
suitable: true,
reasoning: "Local model available. Offline mode provides complete privacy and works without internet.",
requirements: [
"Ollama service running",
"Local model downloaded"
],
benefits: [
"Complete data privacy - no cloud API calls",
"Works without internet connection",
"No API costs for model usage",
"Complies with strict security policies",
"Faster response times (no network latency)"
],
limitations: [
"Limited to local model capabilities",
"No access to latest cloud models",
"Some integrations unavailable (GitHub, issue trackers)",
"No web search or external data",
"Reduced context window compared to cloud models"
]
};
}