UNPKG

giga-code

Version:

A personal AI CLI assistant powered by Grok for local development.

289 lines 14.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const ink_1 = require("ink"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const api_key_validator_1 = require("../../utils/api-key-validator"); const api_keys_1 = require("../../utils/api-keys"); function ProviderSettings({ providers, selectedIndex: initialSelectedIndex, onClose }) { const [apiKeys, setApiKeys] = (0, react_1.useState)({}); const [currentInput, setCurrentInput] = (0, react_1.useState)(""); const [editingIndex, setEditingIndex] = (0, react_1.useState)(null); const [selectedIndex, setSelectedIndex] = (0, react_1.useState)(initialSelectedIndex); const [error, setError] = (0, react_1.useState)(""); const [validationErrors, setValidationErrors] = (0, react_1.useState)({}); const [validationStatus, setValidationStatus] = (0, react_1.useState)({}); const [isValidating, setIsValidating] = (0, react_1.useState)(false); const [isMounted, setIsMounted] = (0, react_1.useState)(true); (0, react_1.useEffect)(() => { loadExistingKeys(); return () => { setIsMounted(false); }; }, []); const loadExistingKeys = () => { try { const homeDir = os.homedir(); const settingsFile = path.join(homeDir, '.giga', 'user-settings.json'); if (fs.existsSync(settingsFile)) { const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8')); const keys = {}; providers.forEach(provider => { const key = settings[provider.keyName]; if (key) { keys[provider.keyName] = key; } }); setApiKeys(keys); } } catch (error) { setError("Could not load existing API keys"); } }; const validateCurrentKey = async (providerName, key) => { if (!key.trim()) { if (isMounted) { setValidationErrors(prev => { const newErrors = { ...prev }; delete newErrors[providerName]; // Remove error instead of setting empty string return newErrors; }); setValidationStatus(prev => ({ ...prev, [providerName]: false })); } return; } if (isMounted) { setIsValidating(true); } try { const result = await (0, api_key_validator_1.validateApiKey)(providerName.toLowerCase(), key); if (!isMounted) return; // Component unmounted, don't update state if (result.isValid) { setValidationErrors(prev => { const newErrors = { ...prev }; delete newErrors[providerName]; // Remove error instead of setting empty string return newErrors; }); setValidationStatus(prev => ({ ...prev, [providerName]: true })); } else { const errorMessage = result.error?.trim() || "Invalid API key"; // Never set empty string as error message if (errorMessage) { setValidationErrors(prev => ({ ...prev, [providerName]: errorMessage })); } setValidationStatus(prev => ({ ...prev, [providerName]: false })); } } catch (error) { if (!isMounted) return; // Component unmounted, don't update state setValidationErrors(prev => ({ ...prev, [providerName]: "Validation failed" })); setValidationStatus(prev => ({ ...prev, [providerName]: false })); } if (isMounted) { setIsValidating(false); } }; const saveApiKeys = async () => { try { const homeDir = os.homedir(); const gigaDir = path.join(homeDir, '.giga'); const settingsFile = path.join(gigaDir, 'user-settings.json'); if (!fs.existsSync(gigaDir)) { fs.mkdirSync(gigaDir, { mode: 0o700 }); } let settings = {}; if (fs.existsSync(settingsFile)) { try { settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8')); } catch { settings = {}; } } Object.entries(apiKeys).forEach(([keyName, value]) => { if (value.trim()) { settings[keyName] = value.trim(); } }); fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2), { mode: 0o600 }); // Refresh global shared info after API key changes (0, api_keys_1.refreshGlobalSharedInfo)(); onClose(); } catch (error) { setError("Could not save API keys"); } }; (0, ink_1.useInput)((inputChar, key) => { if (key.ctrl && inputChar === "c") { onClose(); return; } if (key.escape) { if (editingIndex !== null) { setEditingIndex(null); setCurrentInput(""); } else { // Validate all keys before saving const validationPromises = providers.map(async (provider) => { const key = apiKeys[provider.keyName]; if (key) { await validateCurrentKey(provider.name, key); } }); Promise.all(validationPromises).then(() => { saveApiKeys(); }); } return; } if (editingIndex !== null) { if (key.return) { const provider = providers[editingIndex]; setApiKeys(prev => ({ ...prev, [provider.keyName]: currentInput })); setCurrentInput(""); setEditingIndex(null); setError(""); return; } if (key.backspace || key.delete) { setCurrentInput(prev => prev.slice(0, -1)); return; } if (inputChar && !key.ctrl && !key.meta) { setCurrentInput(prev => prev + inputChar); setError(""); } } else { if (key.upArrow) { // Validate current provider's key before moving const currentProvider = providers[selectedIndex]; const currentKey = apiKeys[currentProvider.keyName]; if (currentKey && currentKey.trim()) { validateCurrentKey(currentProvider.name, currentKey); } const newIndex = selectedIndex === 0 ? providers.length - 1 : selectedIndex - 1; setSelectedIndex(newIndex); return; } if (key.downArrow) { // Validate current provider's key before moving const currentProvider = providers[selectedIndex]; const currentKey = apiKeys[currentProvider.keyName]; if (currentKey && currentKey.trim()) { validateCurrentKey(currentProvider.name, currentKey); } const newIndex = (selectedIndex + 1) % providers.length; setSelectedIndex(newIndex); return; } if (key.return) { setEditingIndex(selectedIndex); const provider = providers[selectedIndex]; setCurrentInput(apiKeys[provider.keyName] || ""); return; } } }); const maskApiKey = (key) => { if (!key) return ""; if (key.length <= 8) return "*".repeat(key.length); return key.substring(0, 4) + "*".repeat(key.length - 8) + key.substring(key.length - 4); }; return (react_1.default.createElement(ink_1.Box, { flexDirection: "column", paddingX: 2, paddingY: 1 }, react_1.default.createElement(ink_1.Text, { color: "yellow" }, "\uD83D\uDD11 Provider API Keys Configuration"), react_1.default.createElement(ink_1.Box, { marginBottom: 1 }, react_1.default.createElement(ink_1.Text, { color: "gray" }, "Configure API keys for different AI providers:")), react_1.default.createElement(ink_1.Box, { flexDirection: "column", marginBottom: 1 }, providers.map((provider, index) => { const isSelected = index === selectedIndex && editingIndex === null; const isEditing = index === editingIndex; const currentKey = apiKeys[provider.keyName] || ""; const hasValidationError = validationErrors[provider.name]; const isValid = validationStatus[provider.name]; let borderColor = "gray"; if (isSelected) borderColor = "blue"; else if (isEditing) borderColor = "green"; else if (hasValidationError) borderColor = "red"; else if (isValid) borderColor = "green"; return (react_1.default.createElement(ink_1.Box, { key: provider.keyName, flexDirection: "column", marginBottom: 1 }, react_1.default.createElement(ink_1.Box, { borderStyle: "round", borderColor: borderColor, paddingX: 1 }, react_1.default.createElement(ink_1.Box, { width: 12 }, react_1.default.createElement(ink_1.Text, { color: isSelected ? "blue" : "white" }, provider.name, ":")), react_1.default.createElement(ink_1.Box, { flexGrow: 1 }, isEditing ? (react_1.default.createElement(ink_1.Text, null, (currentInput || "") + "█")) : (react_1.default.createElement(ink_1.Box, null, react_1.default.createElement(ink_1.Text, { color: "gray" }, currentKey ? (provider.keyName === 'ollamaBaseUrl' ? currentKey : maskApiKey(currentKey)) : "Not configured"), isValid && !hasValidationError && (react_1.default.createElement(ink_1.Text, { color: "green" }, " \u2713"))))), react_1.default.createElement(ink_1.Box, { width: 25 }, react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, provider.description))), hasValidationError && String(hasValidationError).trim() && (react_1.default.createElement(ink_1.Box, { paddingLeft: 2 }, react_1.default.createElement(ink_1.Text, { color: "red" }, "\u274C ", String(hasValidationError).trim()))))); })), error && error.trim() ? (react_1.default.createElement(ink_1.Box, { marginBottom: 1 }, react_1.default.createElement(ink_1.Text, { color: "red" }, "\u274C ", error))) : null, react_1.default.createElement(ink_1.Box, { flexDirection: "column", marginTop: 1 }, editingIndex !== null ? (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(ink_1.Text, { color: "yellow" }, "Editing ", providers[editingIndex].name, " ", providers[editingIndex].keyName === 'ollamaBaseUrl' ? 'Base URL' : 'API Key', ":"), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Type your ", providers[editingIndex].keyName === 'ollamaBaseUrl' ? 'Ollama base URL (e.g., http://localhost:11434)' : 'API key'), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Press Enter to save"), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Press Esc to cancel"))) : (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Use \u2191/\u2193 arrows to navigate"), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Press Enter to edit selected provider"), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Press Esc to save and exit"), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "\u2022 Press Ctrl+C to exit without saving"))), react_1.default.createElement(ink_1.Text, { color: "gray", dimColor: true }, "Keys saved to ~/.giga/user-settings.json"), isValidating && (react_1.default.createElement(ink_1.Text, { color: "yellow" }, "\uD83D\uDD04 Validating API keys..."))))); } exports.default = ProviderSettings; //# sourceMappingURL=provider-settings.js.map