UNPKG

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
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" ] }; }