giga-code
Version:
A personal AI CLI assistant powered by Grok for local development.
289 lines • 14.3 kB
JavaScript
"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