Compare commits

...

12 Commits

Author SHA1 Message Date
di-sukharev
88d6a92567 fix gemini test 2024-09-06 12:10:21 +03:00
di-sukharev
36aab60393 Merge remote-tracking branch 'origin/dev' into refactoring_v2 2024-09-06 12:07:25 +03:00
di-sukharev
ba3c0b7e0b refactor(config.ts): remove unused setDefaultConfigValues function to clean up code
feat(config.ts): create getIsGlobalConfigFileExist function to check for config file existence
feat(migrations): add migration to set missing default values for configuration
fix(migrations): update migration functions to improve clarity and functionality
chore(migrations): register new migration for setting missing default values
style(migrations): format code for better readability in migration files
test(config.test.ts): update tests to improve readability and maintainability
2024-09-06 12:05:28 +03:00
di-sukharev
5209610236 fix(config.ts): ensure setConfig is only called when there are entries to set to prevent unnecessary function calls
feat(run.ts): add conditional to skip migrations if OCO_AI_PROVIDER is set to TEST to improve migration handling during testing
2024-09-04 11:54:54 +03:00
di-sukharev
d1f03f1105 feat(config): rename OCO_OPENAI_API_KEY to OCO_API_KEY for consistency and clarity
fix(config): add validation for OCO_WHY configuration key to ensure it is a boolean
refactor(config): extract default config setting logic into setDefaultConfigValues function
fix(cli): reorder function calls to ensure checkIsLatestVersion runs after runMigrations
chore(migrations): update getConfig calls to disable caching and default value setting
chore(migrations): remove obsolete configuration keys from global config file
fix(migrations): improve migration logging with consistent output formatting
style(migrations): enhance migration success and failure messages for better user feedback
2024-09-04 11:46:43 +03:00
di-sukharev
3df5b241dc fix(config.ts): set default values for OCO_TOKENS_MAX_INPUT and OCO_TOKENS_MAX_OUTPUT to ensure proper configuration
refactor(generateCommitMessageFromGitDiff.ts): simplify MAX_TOKENS_INPUT and MAX_TOKENS_OUTPUT assignments by removing redundant default value logic
2024-09-04 11:04:44 +03:00
di-sukharev
5ddf2cb21a Merge remote-tracking branch 'origin/dev' into refactoring_v2 2024-09-04 10:56:34 +03:00
di-sukharev
c6b6b2f3fd chore(package.json): remove unused dependency "ai" to clean up package.json
fix(commit.ts): update success message to start with a lowercase letter for consistency
2024-09-03 13:13:38 +03:00
di-sukharev
e1ce774538 chore(config): remove debug console logs to clean up the codebase
feat(migrations): add migration to remove obsolete config keys from global file to streamline configuration management
2024-09-02 12:29:41 +03:00
di-sukharev
f1ea54cf1c feat(docs): update README to reflect changes in API key and URL configuration for better clarity and usability
feat(migrations): implement migration to consolidate API key and URL into a single configuration for improved simplicity
refactor(config): rename configuration keys to use a single API key and URL for all LLM providers, enhancing consistency
fix(engine): update engine initialization to use new unified API key and URL configuration
test(config): update tests to validate new configuration structure and ensure backward compatibility with existing setups
2024-09-02 12:25:49 +03:00
di-sukharev
ccb7cd99e5 Merge branch 'dev' into refactoring_v2 2024-09-02 11:13:57 +03:00
di-sukharev
ce6ae0b514 refactor(engine): rename classes from FlowiseAi, Gemini, and OllamaAi to FlowiseEngine, GeminiEngine, and OllamaEngine for consistency and clarity
fix(engine): update imports and instantiation of renamed classes in engine utility functions to ensure proper functionality
2024-09-02 11:13:49 +03:00
21 changed files with 682 additions and 596 deletions

View File

@@ -28,30 +28,19 @@ You can use OpenCommit by simply running it via the CLI like this `oco`. 2 secon
npm install -g opencommit npm install -g opencommit
``` ```
Alternatively run it via `npx opencommit` or `bunx opencommit` 2. Get your API key from [OpenAI](https://platform.openai.com/account/api-keys) or other supported LLM providers (we support them all). Make sure that you add your OpenAI payment details to your account, so the API works.
MacOS may ask to run the command with `sudo` when installing a package globally.
2. Get your API key from [OpenAI](https://platform.openai.com/account/api-keys). Make sure that you add your payment details, so the API works.
3. Set the key to OpenCommit config: 3. Set the key to OpenCommit config:
```sh ```sh
oco config set OCO_OPENAI_API_KEY=<your_api_key> oco config set OCO_API_KEY=<your_api_key>
``` ```
Your API key is stored locally in the `~/.opencommit` config file. Your API key is stored locally in the `~/.opencommit` config file.
## Usage ## Usage
You can call OpenCommit directly to generate a commit message for your staged changes: You can call OpenCommit with `oco` command to generate a commit message for your staged changes:
```sh
git add <files...>
opencommit
```
You can also use the `oco` shortcut:
```sh ```sh
git add <files...> git add <files...>
@@ -70,22 +59,17 @@ You can also run it with local model through ollama:
```sh ```sh
git add <files...> git add <files...>
oco config set OCO_AI_PROVIDER='ollama' oco config set OCO_AI_PROVIDER='ollama' OCO_MODEL='llama3:8b'
``` ```
If you want to use a model other than mistral (default), you can do so by setting the `OCO_AI_PROVIDER` environment variable as follows: Default model is `mistral`.
```sh
oco config set OCO_AI_PROVIDER='ollama'
oco config set OCO_MODEL='llama3:8b'
```
If you have ollama that is set up in docker/ on another machine with GPUs (not locally), you can change the default endpoint url. If you have ollama that is set up in docker/ on another machine with GPUs (not locally), you can change the default endpoint url.
You can do so by setting the `OCO_OLLAMA_API_URL` environment variable as follows: You can do so by setting the `OCO_API_URL` environment variable as follows:
```sh ```sh
oco config set OCO_OLLAMA_API_URL='http://192.168.1.10:11434/api/chat' oco config set OCO_API_URL='http://192.168.1.10:11434/api/chat'
``` ```
where 192.168.1.10 is example of endpoint URL, where you have ollama set up. where 192.168.1.10 is example of endpoint URL, where you have ollama set up.
@@ -122,10 +106,11 @@ Create a `.env` file and add OpenCommit config variables there like this:
```env ```env
... ...
OCO_OPENAI_API_KEY=<your OpenAI API token> OCO_AI_PROVIDER=<openai (default), anthropic, azure, ollama, gemini, flowise>
OCO_API_KEY=<your OpenAI API token> // or other LLM provider API token
OCO_API_URL=<may be used to set proxy path to OpenAI api>
OCO_TOKENS_MAX_INPUT=<max model token limit (default: 4096)> OCO_TOKENS_MAX_INPUT=<max model token limit (default: 4096)>
OCO_TOKENS_MAX_OUTPUT=<max response tokens (default: 500)> OCO_TOKENS_MAX_OUTPUT=<max response tokens (default: 500)>
OCO_OPENAI_BASE_PATH=<may be used to set proxy path to OpenAI api>
OCO_DESCRIPTION=<postface a message with ~3 sentences description of the changes> OCO_DESCRIPTION=<postface a message with ~3 sentences description of the changes>
OCO_EMOJI=<boolean, add GitMoji> OCO_EMOJI=<boolean, add GitMoji>
OCO_MODEL=<either 'gpt-4o', 'gpt-4', 'gpt-4-turbo', 'gpt-3.5-turbo' (default), 'gpt-3.5-turbo-0125', 'gpt-4-1106-preview', 'gpt-4-turbo-preview' or 'gpt-4-0125-preview' or any Anthropic or Ollama model or any string basically, but it should be a valid model name> OCO_MODEL=<either 'gpt-4o', 'gpt-4', 'gpt-4-turbo', 'gpt-3.5-turbo' (default), 'gpt-3.5-turbo-0125', 'gpt-4-1106-preview', 'gpt-4-turbo-preview' or 'gpt-4-0125-preview' or any Anthropic or Ollama model or any string basically, but it should be a valid model name>
@@ -133,11 +118,9 @@ OCO_LANGUAGE=<locale, scroll to the bottom to see options>
OCO_MESSAGE_TEMPLATE_PLACEHOLDER=<message template placeholder, default: '$msg'> OCO_MESSAGE_TEMPLATE_PLACEHOLDER=<message template placeholder, default: '$msg'>
OCO_PROMPT_MODULE=<either conventional-commit or @commitlint, default: conventional-commit> OCO_PROMPT_MODULE=<either conventional-commit or @commitlint, default: conventional-commit>
OCO_ONE_LINE_COMMIT=<one line commit message, default: false> OCO_ONE_LINE_COMMIT=<one line commit message, default: false>
OCO_AI_PROVIDER=<openai (default), anthropic, azure, ollama>
...
``` ```
This are not all the config options, but you get the point. Global configs are same as local configs, but they are stored in the global `~/.opencommit` config file and set with `oco config set` command, e.g. `oco config set OCO_MODEL=gpt-4o`.
### Global config for all repos ### Global config for all repos
@@ -189,26 +172,26 @@ or for as a cheaper option:
oco config set OCO_MODEL=gpt-3.5-turbo oco config set OCO_MODEL=gpt-3.5-turbo
``` ```
### Switch to Azure OpenAI ### Switch to other LLM providers with a custom URL
By default OpenCommit uses [OpenAI](https://openai.com). By default OpenCommit uses [OpenAI](https://openai.com).
You could switch to [Azure OpenAI Service](https://learn.microsoft.com/azure/cognitive-services/openai/)🚀 You could switch to [Azure OpenAI Service](https://learn.microsoft.com/azure/cognitive-services/openai/) or Flowise or Ollama.
```sh ```sh
opencommit config set OCO_AI_PROVIDER=azure oco config set OCO_AI_PROVIDER=azure OCO_API_KEY=<your_azure_api_key> OCO_API_URL=<your_azure_endpoint>
```
Of course need to set 'OCO_OPENAI_API_KEY'. And also need to set the oco config set OCO_AI_PROVIDER=flowise OCO_API_KEY=<your_flowise_api_key> OCO_API_URL=<your_flowise_endpoint>
'OPENAI_BASE_PATH' for the endpoint and set the deployment name to
'model'. oco config set OCO_AI_PROVIDER=ollama OCO_API_KEY=<your_ollama_api_key> OCO_API_URL=<your_ollama_endpoint>
```
### Locale configuration ### Locale configuration
To globally specify the language used to generate commit messages: To globally specify the language used to generate commit messages:
```sh ```sh
# de, German ,Deutsch # de, German, Deutsch
oco config set OCO_LANGUAGE=de oco config set OCO_LANGUAGE=de
oco config set OCO_LANGUAGE=German oco config set OCO_LANGUAGE=German
oco config set OCO_LANGUAGE=Deutsch oco config set OCO_LANGUAGE=Deutsch
@@ -230,6 +213,8 @@ A prompt for pushing to git is on by default but if you would like to turn it of
oco config set OCO_GITPUSH=false oco config set OCO_GITPUSH=false
``` ```
and it will exit right after commit is confirmed without asking if you would like to push to remote.
### Switch to `@commitlint` ### Switch to `@commitlint`
OpenCommit allows you to choose the prompt module used to generate commit messages. By default, OpenCommit uses its conventional-commit message generator. However, you can switch to using the `@commitlint` prompt module if you prefer. This option lets you generate commit messages in respect with the local config. OpenCommit allows you to choose the prompt module used to generate commit messages. By default, OpenCommit uses its conventional-commit message generator. However, you can switch to using the `@commitlint` prompt module if you prefer. This option lets you generate commit messages in respect with the local config.
@@ -404,7 +389,7 @@ jobs:
# set openAI api key in repo actions secrets, # set openAI api key in repo actions secrets,
# for openAI keys go to: https://platform.openai.com/account/api-keys # for openAI keys go to: https://platform.openai.com/account/api-keys
# for repo secret go to: <your_repo_url>/settings/secrets/actions # for repo secret go to: <your_repo_url>/settings/secrets/actions
OCO_OPENAI_API_KEY: ${{ secrets.OCO_OPENAI_API_KEY }} OCO_API_KEY: ${{ secrets.OCO_API_KEY }}
# customization # customization
OCO_TOKENS_MAX_INPUT: 4096 OCO_TOKENS_MAX_INPUT: 4096

View File

@@ -144,7 +144,7 @@ var require_windows = __commonJS({
"node_modules/isexe/windows.js"(exports, module2) { "node_modules/isexe/windows.js"(exports, module2) {
module2.exports = isexe; module2.exports = isexe;
isexe.sync = sync; isexe.sync = sync;
var fs6 = require("fs"); var fs7 = require("fs");
function checkPathExt(path5, options) { function checkPathExt(path5, options) {
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT; var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
if (!pathext) { if (!pathext) {
@@ -169,12 +169,12 @@ var require_windows = __commonJS({
return checkPathExt(path5, options); return checkPathExt(path5, options);
} }
function isexe(path5, options, cb) { function isexe(path5, options, cb) {
fs6.stat(path5, function(er2, stat) { fs7.stat(path5, function(er2, stat) {
cb(er2, er2 ? false : checkStat(stat, path5, options)); cb(er2, er2 ? false : checkStat(stat, path5, options));
}); });
} }
function sync(path5, options) { function sync(path5, options) {
return checkStat(fs6.statSync(path5), path5, options); return checkStat(fs7.statSync(path5), path5, options);
} }
} }
}); });
@@ -184,14 +184,14 @@ var require_mode = __commonJS({
"node_modules/isexe/mode.js"(exports, module2) { "node_modules/isexe/mode.js"(exports, module2) {
module2.exports = isexe; module2.exports = isexe;
isexe.sync = sync; isexe.sync = sync;
var fs6 = require("fs"); var fs7 = require("fs");
function isexe(path5, options, cb) { function isexe(path5, options, cb) {
fs6.stat(path5, function(er2, stat) { fs7.stat(path5, function(er2, stat) {
cb(er2, er2 ? false : checkStat(stat, options)); cb(er2, er2 ? false : checkStat(stat, options));
}); });
} }
function sync(path5, options) { function sync(path5, options) {
return checkStat(fs6.statSync(path5), options); return checkStat(fs7.statSync(path5), options);
} }
function checkStat(stat, options) { function checkStat(stat, options) {
return stat.isFile() && checkMode(stat, options); return stat.isFile() && checkMode(stat, options);
@@ -215,7 +215,7 @@ var require_mode = __commonJS({
// node_modules/isexe/index.js // node_modules/isexe/index.js
var require_isexe = __commonJS({ var require_isexe = __commonJS({
"node_modules/isexe/index.js"(exports, module2) { "node_modules/isexe/index.js"(exports, module2) {
var fs6 = require("fs"); var fs7 = require("fs");
var core; var core;
if (process.platform === "win32" || global.TESTING_WINDOWS) { if (process.platform === "win32" || global.TESTING_WINDOWS) {
core = require_windows(); core = require_windows();
@@ -477,16 +477,16 @@ var require_shebang_command = __commonJS({
var require_readShebang = __commonJS({ var require_readShebang = __commonJS({
"node_modules/cross-spawn/lib/util/readShebang.js"(exports, module2) { "node_modules/cross-spawn/lib/util/readShebang.js"(exports, module2) {
"use strict"; "use strict";
var fs6 = require("fs"); var fs7 = require("fs");
var shebangCommand = require_shebang_command(); var shebangCommand = require_shebang_command();
function readShebang(command) { function readShebang(command) {
const size = 150; const size = 150;
const buffer = Buffer.alloc(size); const buffer = Buffer.alloc(size);
let fd; let fd;
try { try {
fd = fs6.openSync(command, "r"); fd = fs7.openSync(command, "r");
fs6.readSync(fd, buffer, 0, size, 0); fs7.readSync(fd, buffer, 0, size, 0);
fs6.closeSync(fd); fs7.closeSync(fd);
} catch (e3) { } catch (e3) {
} }
return shebangCommand(buffer.toString()); return shebangCommand(buffer.toString());
@@ -1040,7 +1040,7 @@ var require_package = __commonJS({
// node_modules/dotenv/lib/main.js // node_modules/dotenv/lib/main.js
var require_main = __commonJS({ var require_main = __commonJS({
"node_modules/dotenv/lib/main.js"(exports, module2) { "node_modules/dotenv/lib/main.js"(exports, module2) {
var fs6 = require("fs"); var fs7 = require("fs");
var path5 = require("path"); var path5 = require("path");
var os4 = require("os"); var os4 = require("os");
var crypto2 = require("crypto"); var crypto2 = require("crypto");
@@ -1147,7 +1147,7 @@ var require_main = __commonJS({
if (options && options.path && options.path.length > 0) { if (options && options.path && options.path.length > 0) {
if (Array.isArray(options.path)) { if (Array.isArray(options.path)) {
for (const filepath of options.path) { for (const filepath of options.path) {
if (fs6.existsSync(filepath)) { if (fs7.existsSync(filepath)) {
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`; possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
} }
} }
@@ -1157,7 +1157,7 @@ var require_main = __commonJS({
} else { } else {
possibleVaultPath = path5.resolve(process.cwd(), ".env.vault"); possibleVaultPath = path5.resolve(process.cwd(), ".env.vault");
} }
if (fs6.existsSync(possibleVaultPath)) { if (fs7.existsSync(possibleVaultPath)) {
return possibleVaultPath; return possibleVaultPath;
} }
return null; return null;
@@ -1201,7 +1201,7 @@ var require_main = __commonJS({
const parsedAll = {}; const parsedAll = {};
for (const path6 of optionPaths) { for (const path6 of optionPaths) {
try { try {
const parsed = DotenvModule.parse(fs6.readFileSync(path6, { encoding })); const parsed = DotenvModule.parse(fs7.readFileSync(path6, { encoding }));
DotenvModule.populate(parsedAll, parsed, options); DotenvModule.populate(parsedAll, parsed, options);
} catch (e3) { } catch (e3) {
if (debug3) { if (debug3) {
@@ -21024,7 +21024,7 @@ var require_form_data = __commonJS({
var http3 = require("http"); var http3 = require("http");
var https3 = require("https"); var https3 = require("https");
var parseUrl = require("url").parse; var parseUrl = require("url").parse;
var fs6 = require("fs"); var fs7 = require("fs");
var Stream3 = require("stream").Stream; var Stream3 = require("stream").Stream;
var mime = require_mime_types(); var mime = require_mime_types();
var asynckit = require_asynckit(); var asynckit = require_asynckit();
@@ -21089,7 +21089,7 @@ var require_form_data = __commonJS({
if (value.end != void 0 && value.end != Infinity && value.start != void 0) { if (value.end != void 0 && value.end != Infinity && value.start != void 0) {
callback(null, value.end + 1 - (value.start ? value.start : 0)); callback(null, value.end + 1 - (value.start ? value.start : 0));
} else { } else {
fs6.stat(value.path, function(err, stat) { fs7.stat(value.path, function(err, stat) {
var fileSize; var fileSize;
if (err) { if (err) {
callback(err); callback(err);
@@ -22849,7 +22849,7 @@ var require_tiktoken = __commonJS({
var imports = {}; var imports = {};
imports["./tiktoken_bg.js"] = wasm; imports["./tiktoken_bg.js"] = wasm;
var path5 = require("path"); var path5 = require("path");
var fs6 = require("fs"); var fs7 = require("fs");
var candidates = __dirname.split(path5.sep).reduce((memo, _7, index, array) => { var candidates = __dirname.split(path5.sep).reduce((memo, _7, index, array) => {
const prefix = array.slice(0, index + 1).join(path5.sep) + path5.sep; const prefix = array.slice(0, index + 1).join(path5.sep) + path5.sep;
if (!prefix.includes("node_modules" + path5.sep)) { if (!prefix.includes("node_modules" + path5.sep)) {
@@ -22869,7 +22869,7 @@ var require_tiktoken = __commonJS({
var bytes = null; var bytes = null;
for (const candidate of candidates) { for (const candidate of candidates) {
try { try {
bytes = fs6.readFileSync(candidate); bytes = fs7.readFileSync(candidate);
break; break;
} catch { } catch {
} }
@@ -27419,7 +27419,6 @@ var package_default = {
"@google/generative-ai": "^0.11.4", "@google/generative-ai": "^0.11.4",
"@octokit/webhooks-schemas": "^6.11.0", "@octokit/webhooks-schemas": "^6.11.0",
"@octokit/webhooks-types": "^6.11.0", "@octokit/webhooks-types": "^6.11.0",
ai: "^2.2.14",
axios: "^1.3.4", axios: "^1.3.4",
chalk: "^5.2.0", chalk: "^5.2.0",
cleye: "^1.3.2", cleye: "^1.3.2",
@@ -27428,7 +27427,7 @@ var package_default = {
ignore: "^5.2.4", ignore: "^5.2.4",
ini: "^3.0.1", ini: "^3.0.1",
inquirer: "^9.1.4", inquirer: "^9.1.4",
openai: "^4.56.0" openai: "^4.57.0"
} }
}; };
@@ -29877,33 +29876,6 @@ function getI18nLocal(value) {
} }
// src/commands/config.ts // src/commands/config.ts
var CONFIG_KEYS = /* @__PURE__ */ ((CONFIG_KEYS2) => {
CONFIG_KEYS2["OCO_OPENAI_API_KEY"] = "OCO_OPENAI_API_KEY";
CONFIG_KEYS2["OCO_ANTHROPIC_API_KEY"] = "OCO_ANTHROPIC_API_KEY";
CONFIG_KEYS2["OCO_AZURE_API_KEY"] = "OCO_AZURE_API_KEY";
CONFIG_KEYS2["OCO_GEMINI_API_KEY"] = "OCO_GEMINI_API_KEY";
CONFIG_KEYS2["OCO_GEMINI_BASE_PATH"] = "OCO_GEMINI_BASE_PATH";
CONFIG_KEYS2["OCO_TOKENS_MAX_INPUT"] = "OCO_TOKENS_MAX_INPUT";
CONFIG_KEYS2["OCO_TOKENS_MAX_OUTPUT"] = "OCO_TOKENS_MAX_OUTPUT";
CONFIG_KEYS2["OCO_OPENAI_BASE_PATH"] = "OCO_OPENAI_BASE_PATH";
CONFIG_KEYS2["OCO_DESCRIPTION"] = "OCO_DESCRIPTION";
CONFIG_KEYS2["OCO_EMOJI"] = "OCO_EMOJI";
CONFIG_KEYS2["OCO_MODEL"] = "OCO_MODEL";
CONFIG_KEYS2["OCO_LANGUAGE"] = "OCO_LANGUAGE";
CONFIG_KEYS2["OCO_WHY"] = "OCO_WHY";
CONFIG_KEYS2["OCO_MESSAGE_TEMPLATE_PLACEHOLDER"] = "OCO_MESSAGE_TEMPLATE_PLACEHOLDER";
CONFIG_KEYS2["OCO_PROMPT_MODULE"] = "OCO_PROMPT_MODULE";
CONFIG_KEYS2["OCO_AI_PROVIDER"] = "OCO_AI_PROVIDER";
CONFIG_KEYS2["OCO_GITPUSH"] = "OCO_GITPUSH";
CONFIG_KEYS2["OCO_ONE_LINE_COMMIT"] = "OCO_ONE_LINE_COMMIT";
CONFIG_KEYS2["OCO_AZURE_ENDPOINT"] = "OCO_AZURE_ENDPOINT";
CONFIG_KEYS2["OCO_TEST_MOCK_TYPE"] = "OCO_TEST_MOCK_TYPE";
CONFIG_KEYS2["OCO_API_URL"] = "OCO_API_URL";
CONFIG_KEYS2["OCO_OLLAMA_API_URL"] = "OCO_OLLAMA_API_URL";
CONFIG_KEYS2["OCO_FLOWISE_ENDPOINT"] = "OCO_FLOWISE_ENDPOINT";
CONFIG_KEYS2["OCO_FLOWISE_API_KEY"] = "OCO_FLOWISE_API_KEY";
return CONFIG_KEYS2;
})(CONFIG_KEYS || {});
var MODEL_LIST = { var MODEL_LIST = {
openai: [ openai: [
"gpt-4o-mini", "gpt-4o-mini",
@@ -29969,56 +29941,18 @@ var validateConfig = (key, condition, validationMessage) => {
} }
}; };
var configValidators = { var configValidators = {
["OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */](value, config7 = {}) { ["OCO_API_KEY" /* OCO_API_KEY */](value, config7 = {}) {
if (config7.OCO_AI_PROVIDER !== "openai") if (config7.OCO_AI_PROVIDER !== "openai")
return value; return value;
validateConfig( validateConfig(
"OCO_OPENAI_API_KEY", "OCO_API_KEY",
typeof value === "string" && value.length > 0, typeof value === "string" && value.length > 0,
"Empty value is not allowed" "Empty value is not allowed"
); );
validateConfig( validateConfig(
"OCO_OPENAI_API_KEY", "OCO_API_KEY",
value, value,
'You need to provide the OCO_OPENAI_API_KEY when OCO_AI_PROVIDER is set to "openai" (default). Run `oco config set OCO_OPENAI_API_KEY=your_key`' 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`'
);
return value;
},
["OCO_AZURE_API_KEY" /* OCO_AZURE_API_KEY */](value, config7 = {}) {
if (config7.OCO_AI_PROVIDER !== "azure")
return value;
validateConfig(
"OCO_AZURE_API_KEY",
!!value,
'You need to provide the OCO_AZURE_API_KEY when OCO_AI_PROVIDER is set to "azure". Run: `oco config set OCO_AZURE_API_KEY=your_key`'
);
return value;
},
["OCO_GEMINI_API_KEY" /* OCO_GEMINI_API_KEY */](value, config7 = {}) {
if (config7.OCO_AI_PROVIDER !== "gemini")
return value;
validateConfig(
"OCO_GEMINI_API_KEY",
value || config7.OCO_GEMINI_API_KEY || config7.OCO_AI_PROVIDER === "test",
'You need to provide the OCO_GEMINI_API_KEY when OCO_AI_PROVIDER is set to "gemini". Run: `oco config set OCO_GEMINI_API_KEY=your_key`'
);
return value;
},
["OCO_ANTHROPIC_API_KEY" /* OCO_ANTHROPIC_API_KEY */](value, config7 = {}) {
if (config7.OCO_AI_PROVIDER !== "anthropic")
return value;
validateConfig(
"ANTHROPIC_API_KEY",
!!value,
'You need to provide the OCO_ANTHROPIC_API_KEY key when OCO_AI_PROVIDER is set to "anthropic". Run: `oco config set OCO_ANTHROPIC_API_KEY=your_key`'
);
return value;
},
["OCO_FLOWISE_API_KEY" /* OCO_FLOWISE_API_KEY */](value, config7 = {}) {
validateConfig(
"OCO_FLOWISE_API_KEY" /* OCO_FLOWISE_API_KEY */,
value || config7.OCO_AI_PROVIDER !== "flowise",
'You need to provide the OCO_FLOWISE_API_KEY when OCO_AI_PROVIDER is set to "flowise". Run: `oco config set OCO_FLOWISE_API_KEY=your_key`'
); );
return value; return value;
}, },
@@ -30065,11 +29999,11 @@ var configValidators = {
); );
return getI18nLocal(value); return getI18nLocal(value);
}, },
["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */](value) { ["OCO_API_URL" /* OCO_API_URL */](value) {
validateConfig( validateConfig(
"OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */, "OCO_API_URL" /* OCO_API_URL */,
typeof value === "string", typeof value === "string",
"Must be string" `${value} is not a valid URL. It should start with 'http://' or 'https://'.`
); );
return value; return value;
}, },
@@ -30131,22 +30065,6 @@ var configValidators = {
); );
return value; return value;
}, },
["OCO_AZURE_ENDPOINT" /* OCO_AZURE_ENDPOINT */](value) {
validateConfig(
"OCO_AZURE_ENDPOINT" /* OCO_AZURE_ENDPOINT */,
value.includes("openai.azure.com"),
'Must be in format "https://<resource name>.openai.azure.com/"'
);
return value;
},
["OCO_FLOWISE_ENDPOINT" /* OCO_FLOWISE_ENDPOINT */](value) {
validateConfig(
"OCO_FLOWISE_ENDPOINT" /* OCO_FLOWISE_ENDPOINT */,
typeof value === "string" && value.includes(":"),
"Value must be string and should include both I.P. and port number"
);
return value;
},
["OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */](value) { ["OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */](value) {
validateConfig( validateConfig(
"OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */, "OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */,
@@ -30157,15 +30075,25 @@ var configValidators = {
); );
return value; return value;
}, },
["OCO_OLLAMA_API_URL" /* OCO_OLLAMA_API_URL */](value) { ["OCO_WHY" /* OCO_WHY */](value) {
validateConfig( validateConfig(
"OCO_OLLAMA_API_URL" /* OCO_OLLAMA_API_URL */, "OCO_WHY" /* OCO_WHY */,
typeof value === "string" && value.startsWith("http"), typeof value === "boolean",
`${value} is not a valid URL. It should start with 'http://' or 'https://'.` "Must be true or false"
); );
return value; return value;
} }
}; };
var OCO_AI_PROVIDER_ENUM = /* @__PURE__ */ ((OCO_AI_PROVIDER_ENUM2) => {
OCO_AI_PROVIDER_ENUM2["OLLAMA"] = "ollama";
OCO_AI_PROVIDER_ENUM2["OPENAI"] = "openai";
OCO_AI_PROVIDER_ENUM2["ANTHROPIC"] = "anthropic";
OCO_AI_PROVIDER_ENUM2["GEMINI"] = "gemini";
OCO_AI_PROVIDER_ENUM2["AZURE"] = "azure";
OCO_AI_PROVIDER_ENUM2["TEST"] = "test";
OCO_AI_PROVIDER_ENUM2["FLOWISE"] = "flowise";
return OCO_AI_PROVIDER_ENUM2;
})(OCO_AI_PROVIDER_ENUM || {});
var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit");
var defaultEnvPath = (0, import_path.resolve)(process.cwd(), ".env"); var defaultEnvPath = (0, import_path.resolve)(process.cwd(), ".env");
var DEFAULT_CONFIG = { var DEFAULT_CONFIG = {
@@ -30188,7 +30116,7 @@ var initGlobalConfig = (configPath = defaultConfigPath) => {
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8"); (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8");
return DEFAULT_CONFIG; return DEFAULT_CONFIG;
}; };
var parseEnvVarValue = (value) => { var parseConfigVarValue = (value) => {
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch (error) { } catch (error) {
@@ -30199,32 +30127,32 @@ var getEnvConfig = (envPath) => {
dotenv.config({ path: envPath }); dotenv.config({ path: envPath });
return { return {
OCO_MODEL: process.env.OCO_MODEL, OCO_MODEL: process.env.OCO_MODEL,
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, OCO_API_URL: process.env.OCO_API_URL,
OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY, OCO_API_KEY: process.env.OCO_API_KEY,
OCO_AZURE_API_KEY: process.env.OCO_AZURE_API_KEY, OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER,
OCO_GEMINI_API_KEY: process.env.OCO_GEMINI_API_KEY, OCO_TOKENS_MAX_INPUT: parseConfigVarValue(process.env.OCO_TOKENS_MAX_INPUT),
OCO_FLOWISE_API_KEY: process.env.OCO_FLOWISE_API_KEY, OCO_TOKENS_MAX_OUTPUT: parseConfigVarValue(
OCO_TOKENS_MAX_INPUT: parseEnvVarValue(process.env.OCO_TOKENS_MAX_INPUT), process.env.OCO_TOKENS_MAX_OUTPUT
OCO_TOKENS_MAX_OUTPUT: parseEnvVarValue(process.env.OCO_TOKENS_MAX_OUTPUT), ),
OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, OCO_DESCRIPTION: parseConfigVarValue(process.env.OCO_DESCRIPTION),
OCO_GEMINI_BASE_PATH: process.env.OCO_GEMINI_BASE_PATH, OCO_EMOJI: parseConfigVarValue(process.env.OCO_EMOJI),
OCO_AZURE_ENDPOINT: process.env.OCO_AZURE_ENDPOINT,
OCO_FLOWISE_ENDPOINT: process.env.OCO_FLOWISE_ENDPOINT,
OCO_OLLAMA_API_URL: process.env.OCO_OLLAMA_API_URL,
OCO_DESCRIPTION: parseEnvVarValue(process.env.OCO_DESCRIPTION),
OCO_EMOJI: parseEnvVarValue(process.env.OCO_EMOJI),
OCO_LANGUAGE: process.env.OCO_LANGUAGE, OCO_LANGUAGE: process.env.OCO_LANGUAGE,
OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER, OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER,
OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE, OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE,
OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER, OCO_ONE_LINE_COMMIT: parseConfigVarValue(process.env.OCO_ONE_LINE_COMMIT),
OCO_ONE_LINE_COMMIT: parseEnvVarValue(process.env.OCO_ONE_LINE_COMMIT),
OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE, OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE,
OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) OCO_GITPUSH: parseConfigVarValue(process.env.OCO_GITPUSH)
}; };
}; };
var getGlobalConfig = (configPath) => { var setGlobalConfig = (config7, configPath = defaultConfigPath) => {
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config7), "utf8");
};
var getIsGlobalConfigFileExist = (configPath = defaultConfigPath) => {
return (0, import_fs.existsSync)(configPath);
};
var getGlobalConfig = (configPath = defaultConfigPath) => {
let globalConfig; let globalConfig;
const isGlobalConfigFileExist = (0, import_fs.existsSync)(configPath); const isGlobalConfigFileExist = getIsGlobalConfigFileExist(configPath);
if (!isGlobalConfigFileExist) if (!isGlobalConfigFileExist)
globalConfig = initGlobalConfig(configPath); globalConfig = initGlobalConfig(configPath);
else { else {
@@ -30233,10 +30161,13 @@ var getGlobalConfig = (configPath) => {
} }
return globalConfig; return globalConfig;
}; };
var mergeConfigs = (main, fallback) => Object.keys(CONFIG_KEYS).reduce((acc, key) => { var mergeConfigs = (main, fallback) => {
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]); const allKeys = /* @__PURE__ */ new Set([...Object.keys(main), ...Object.keys(fallback)]);
return acc; return Array.from(allKeys).reduce((acc, key) => {
}, {}); acc[key] = parseConfigVarValue(main[key] ?? fallback[key]);
return acc;
}, {});
};
var getConfig = ({ var getConfig = ({
envPath = defaultEnvPath, envPath = defaultEnvPath,
globalPath = defaultConfigPath globalPath = defaultConfigPath
@@ -30250,6 +30181,7 @@ var setConfig = (keyValues, globalConfigPath = defaultConfigPath) => {
const config7 = getConfig({ const config7 = getConfig({
globalPath: globalConfigPath globalPath: globalConfigPath
}); });
const configToSet = {};
for (let [key, value] of keyValues) { for (let [key, value] of keyValues) {
if (!configValidators.hasOwnProperty(key)) { if (!configValidators.hasOwnProperty(key)) {
const supportedKeys = Object.keys(configValidators).join("\n"); const supportedKeys = Object.keys(configValidators).join("\n");
@@ -30263,7 +30195,10 @@ For more help refer to our docs: https://github.com/di-sukharev/opencommit`
} }
let parsedConfigValue; let parsedConfigValue;
try { try {
parsedConfigValue = JSON.parse(value); if (typeof value === "string")
parsedConfigValue = JSON.parse(value);
else
parsedConfigValue = value;
} catch (error) { } catch (error) {
parsedConfigValue = value; parsedConfigValue = value;
} }
@@ -30271,9 +30206,9 @@ For more help refer to our docs: https://github.com/di-sukharev/opencommit`
parsedConfigValue, parsedConfigValue,
config7 config7
); );
config7[key] = validValue; configToSet[key] = validValue;
} }
(0, import_fs.writeFileSync)(globalConfigPath, (0, import_ini.stringify)(config7), "utf8"); setGlobalConfig(mergeConfigs(configToSet, config7), globalConfigPath);
ce(`${source_default.green("\u2714")} config successfully set`); ce(`${source_default.green("\u2714")} config successfully set`);
}; };
var configCommand = G3( var configCommand = G3(
@@ -39359,7 +39294,7 @@ var AzureEngine = class {
}; };
// src/engine/flowise.ts // src/engine/flowise.ts
var FlowiseAi = class { var FlowiseEngine = class {
constructor(config7) { constructor(config7) {
this.config = config7; this.config = config7;
this.client = axios_default.create({ this.client = axios_default.create({
@@ -40154,7 +40089,7 @@ var GoogleGenerativeAI = class {
}; };
// src/engine/gemini.ts // src/engine/gemini.ts
var Gemini = class { var GeminiEngine = class {
constructor(config7) { constructor(config7) {
this.client = new GoogleGenerativeAI(config7.apiKey); this.client = new GoogleGenerativeAI(config7.apiKey);
this.config = config7; this.config = config7;
@@ -40212,7 +40147,7 @@ var Gemini = class {
}; };
// src/engine/ollama.ts // src/engine/ollama.ts
var OllamaAi = class { var OllamaEngine = class {
constructor(config7) { constructor(config7) {
this.config = config7; this.config = config7;
this.client = axios_default.create({ this.client = axios_default.create({
@@ -44547,44 +44482,24 @@ function getEngine() {
model: config7.OCO_MODEL, model: config7.OCO_MODEL,
maxTokensOutput: config7.OCO_TOKENS_MAX_OUTPUT, maxTokensOutput: config7.OCO_TOKENS_MAX_OUTPUT,
maxTokensInput: config7.OCO_TOKENS_MAX_INPUT, maxTokensInput: config7.OCO_TOKENS_MAX_INPUT,
baseURL: config7.OCO_OPENAI_BASE_PATH baseURL: config7.OCO_API_URL,
apiKey: config7.OCO_API_KEY
}; };
switch (provider) { switch (provider) {
case "ollama" /* OLLAMA */: case "ollama" /* OLLAMA */:
return new OllamaAi({ return new OllamaEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: "",
baseURL: config7.OCO_OLLAMA_API_URL
});
case "anthropic" /* ANTHROPIC */: case "anthropic" /* ANTHROPIC */:
return new AnthropicEngine({ return new AnthropicEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config7.OCO_ANTHROPIC_API_KEY
});
case "test" /* TEST */: case "test" /* TEST */:
return new TestAi(config7.OCO_TEST_MOCK_TYPE); return new TestAi(config7.OCO_TEST_MOCK_TYPE);
case "gemini" /* GEMINI */: case "gemini" /* GEMINI */:
return new Gemini({ return new GeminiEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config7.OCO_GEMINI_API_KEY,
baseURL: config7.OCO_GEMINI_BASE_PATH
});
case "azure" /* AZURE */: case "azure" /* AZURE */:
return new AzureEngine({ return new AzureEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config7.OCO_AZURE_API_KEY
});
case "flowise" /* FLOWISE */: case "flowise" /* FLOWISE */:
return new FlowiseAi({ return new FlowiseEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
baseURL: config7.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG2.baseURL,
apiKey: config7.OCO_FLOWISE_API_KEY
});
default: default:
return new OpenAiEngine({ return new OpenAiEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config7.OCO_OPENAI_API_KEY
});
} }
} }
@@ -45068,8 +44983,8 @@ function mergeDiffs(arr, maxStringLength) {
// src/generateCommitMessageFromGitDiff.ts // src/generateCommitMessageFromGitDiff.ts
var config5 = getConfig(); var config5 = getConfig();
var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT || 40960 /* DEFAULT_MAX_TOKENS_INPUT */; var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT;
var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT || 4096 /* DEFAULT_MAX_TOKENS_OUTPUT */; var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT;
var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec) => { var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec) => {
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec);
const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT]; const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT];
@@ -45401,13 +45316,13 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
const pushSpinner = le(); const pushSpinner = le();
pushSpinner.start(`Running 'git push ${selectedRemote}'`); pushSpinner.start(`Running 'git push ${selectedRemote}'`);
const { stdout: stdout2 } = await execa("git", ["push", selectedRemote]); const { stdout: stdout2 } = await execa("git", ["push", selectedRemote]);
if (stdout2)
ce(stdout2);
pushSpinner.stop( pushSpinner.stop(
`${source_default.green( `${source_default.green(
"\u2714" "\u2714"
)} Successfully pushed all commits to ${selectedRemote}` )} successfully pushed all commits to ${selectedRemote}`
); );
if (stdout2)
ce(stdout2);
} }
} else { } else {
const regenerateMessage = await Q3({ const regenerateMessage = await Q3({
@@ -45635,9 +45550,9 @@ var prepareCommitMessageHook = async (isStageAllFlag = false) => {
return; return;
ae("opencommit"); ae("opencommit");
const config7 = getConfig(); const config7 = getConfig();
if (!config7.OCO_OPENAI_API_KEY && !config7.OCO_ANTHROPIC_API_KEY && !config7.OCO_AZURE_API_KEY) { if (!config7.OCO_API_KEY) {
ce( ce(
"No OCO_OPENAI_API_KEY or OCO_ANTHROPIC_API_KEY or OCO_AZURE_API_KEY exists. Set your key via `oco config set <key>=<value>, e.g. `oco config set OCO_OPENAI_API_KEY=<value>`. For more info see https://github.com/di-sukharev/opencommit" "No OCO_API_KEY is set. Set your key via `oco config set OCO_API_KEY=<value>. For more info see https://github.com/di-sukharev/opencommit"
); );
return; return;
} }
@@ -45688,6 +45603,152 @@ Current version: ${currentVersion}. Latest version: ${latestVersion}.
} }
}; };
// src/migrations/_run.ts
var import_fs5 = __toESM(require("fs"), 1);
var import_os2 = require("os");
var import_path5 = require("path");
// src/migrations/00_use_single_api_key_and_url.ts
function use_single_api_key_and_url_default() {
const config7 = getConfig({ setDefaultValues: false });
const aiProvider = config7.OCO_AI_PROVIDER;
let apiKey;
let apiUrl;
if (aiProvider === "ollama" /* OLLAMA */) {
apiKey = config7["OCO_OLLAMA_API_KEY"];
apiUrl = config7["OCO_OLLAMA_API_URL"];
} else if (aiProvider === "anthropic" /* ANTHROPIC */) {
apiKey = config7["OCO_ANTHROPIC_API_KEY"];
apiUrl = config7["OCO_ANTHROPIC_BASE_PATH"];
} else if (aiProvider === "openai" /* OPENAI */) {
apiKey = config7["OCO_OPENAI_API_KEY"];
apiUrl = config7["OCO_OPENAI_BASE_PATH"];
} else if (aiProvider === "azure" /* AZURE */) {
apiKey = config7["OCO_AZURE_API_KEY"];
apiUrl = config7["OCO_AZURE_ENDPOINT"];
} else if (aiProvider === "gemini" /* GEMINI */) {
apiKey = config7["OCO_GEMINI_API_KEY"];
apiUrl = config7["OCO_GEMINI_BASE_PATH"];
} else if (aiProvider === "flowise" /* FLOWISE */) {
apiKey = config7["OCO_FLOWISE_API_KEY"];
apiUrl = config7["OCO_FLOWISE_ENDPOINT"];
} else {
throw new Error(
`Migration failed, set AI provider first. Run "oco config set OCO_AI_PROVIDER=<provider>", where <provider> is one of: ${Object.values(
OCO_AI_PROVIDER_ENUM
).join(", ")}`
);
}
if (apiKey)
setConfig([["OCO_API_KEY" /* OCO_API_KEY */, apiKey]]);
if (apiUrl)
setConfig([["OCO_API_URL" /* OCO_API_URL */, apiUrl]]);
}
// src/migrations/01_remove_obsolete_config_keys_from_global_file.ts
function remove_obsolete_config_keys_from_global_file_default() {
const obsoleteKeys = [
"OCO_OLLAMA_API_KEY",
"OCO_OLLAMA_API_URL",
"OCO_ANTHROPIC_API_KEY",
"OCO_ANTHROPIC_BASE_PATH",
"OCO_OPENAI_API_KEY",
"OCO_OPENAI_BASE_PATH",
"OCO_AZURE_API_KEY",
"OCO_AZURE_ENDPOINT",
"OCO_GEMINI_API_KEY",
"OCO_GEMINI_BASE_PATH",
"OCO_FLOWISE_API_KEY",
"OCO_FLOWISE_ENDPOINT"
];
const globalConfig = getGlobalConfig();
const configToOverride = { ...globalConfig };
for (const key of obsoleteKeys)
delete configToOverride[key];
setGlobalConfig(configToOverride);
}
// src/migrations/02_set_missing_default_values.ts
function set_missing_default_values_default() {
const setDefaultConfigValues = (config7) => {
const entriesToSet = [];
for (const entry of Object.entries(DEFAULT_CONFIG)) {
const [key, _value] = entry;
if (config7[key] === "undefined")
entriesToSet.push(entry);
}
if (entriesToSet.length > 0)
setConfig(entriesToSet);
};
setDefaultConfigValues(getGlobalConfig());
}
// src/migrations/_migrations.ts
var migrations = [
{
name: "00_use_single_api_key_and_url",
run: use_single_api_key_and_url_default
},
{
name: "01_remove_obsolete_config_keys_from_global_file",
run: remove_obsolete_config_keys_from_global_file_default
},
{
name: "02_set_missing_default_values",
run: set_missing_default_values_default
}
];
// src/migrations/_run.ts
var migrationsFile = (0, import_path5.join)((0, import_os2.homedir)(), ".opencommit_migrations");
var getCompletedMigrations = () => {
if (!import_fs5.default.existsSync(migrationsFile)) {
return [];
}
const data = import_fs5.default.readFileSync(migrationsFile, "utf-8");
return data ? JSON.parse(data) : [];
};
var saveCompletedMigration = (migrationName) => {
const completedMigrations = getCompletedMigrations();
completedMigrations.push(migrationName);
import_fs5.default.writeFileSync(
migrationsFile,
JSON.stringify(completedMigrations, null, 2)
);
};
var runMigrations = async () => {
if (!getIsGlobalConfigFileExist())
return;
const config7 = getConfig();
if (config7.OCO_AI_PROVIDER === "test" /* TEST */)
return;
const completedMigrations = getCompletedMigrations();
let isMigrated = false;
for (const migration of migrations) {
if (!completedMigrations.includes(migration.name)) {
try {
console.log("Applying migration", migration.name);
migration.run();
console.log("Migration applied successfully", migration.name);
saveCompletedMigration(migration.name);
} catch (error) {
ce(
`${source_default.red("Failed to apply migration")} ${migration.name}: ${error}`
);
}
isMigrated = true;
}
}
if (isMigrated) {
ce(
`${source_default.green(
"\u2714"
)} Migrations to your config were applied successfully. Please rerun.`
);
process.exit(0);
}
};
// src/cli.ts // src/cli.ts
var extraArgs = process.argv.slice(2); var extraArgs = process.argv.slice(2);
Z2( Z2(
@@ -45708,6 +45769,7 @@ Z2(
help: { description: package_default.description } help: { description: package_default.description }
}, },
async ({ flags }) => { async ({ flags }) => {
await runMigrations();
await checkIsLatestVersion(); await checkIsLatestVersion();
if (await isHookCalled()) { if (await isHookCalled()) {
prepareCommitMessageHook(); prepareCommitMessageHook();

View File

@@ -48689,33 +48689,6 @@ function getI18nLocal(value) {
} }
// src/commands/config.ts // src/commands/config.ts
var CONFIG_KEYS = /* @__PURE__ */ ((CONFIG_KEYS2) => {
CONFIG_KEYS2["OCO_OPENAI_API_KEY"] = "OCO_OPENAI_API_KEY";
CONFIG_KEYS2["OCO_ANTHROPIC_API_KEY"] = "OCO_ANTHROPIC_API_KEY";
CONFIG_KEYS2["OCO_AZURE_API_KEY"] = "OCO_AZURE_API_KEY";
CONFIG_KEYS2["OCO_GEMINI_API_KEY"] = "OCO_GEMINI_API_KEY";
CONFIG_KEYS2["OCO_GEMINI_BASE_PATH"] = "OCO_GEMINI_BASE_PATH";
CONFIG_KEYS2["OCO_TOKENS_MAX_INPUT"] = "OCO_TOKENS_MAX_INPUT";
CONFIG_KEYS2["OCO_TOKENS_MAX_OUTPUT"] = "OCO_TOKENS_MAX_OUTPUT";
CONFIG_KEYS2["OCO_OPENAI_BASE_PATH"] = "OCO_OPENAI_BASE_PATH";
CONFIG_KEYS2["OCO_DESCRIPTION"] = "OCO_DESCRIPTION";
CONFIG_KEYS2["OCO_EMOJI"] = "OCO_EMOJI";
CONFIG_KEYS2["OCO_MODEL"] = "OCO_MODEL";
CONFIG_KEYS2["OCO_LANGUAGE"] = "OCO_LANGUAGE";
CONFIG_KEYS2["OCO_WHY"] = "OCO_WHY";
CONFIG_KEYS2["OCO_MESSAGE_TEMPLATE_PLACEHOLDER"] = "OCO_MESSAGE_TEMPLATE_PLACEHOLDER";
CONFIG_KEYS2["OCO_PROMPT_MODULE"] = "OCO_PROMPT_MODULE";
CONFIG_KEYS2["OCO_AI_PROVIDER"] = "OCO_AI_PROVIDER";
CONFIG_KEYS2["OCO_GITPUSH"] = "OCO_GITPUSH";
CONFIG_KEYS2["OCO_ONE_LINE_COMMIT"] = "OCO_ONE_LINE_COMMIT";
CONFIG_KEYS2["OCO_AZURE_ENDPOINT"] = "OCO_AZURE_ENDPOINT";
CONFIG_KEYS2["OCO_TEST_MOCK_TYPE"] = "OCO_TEST_MOCK_TYPE";
CONFIG_KEYS2["OCO_API_URL"] = "OCO_API_URL";
CONFIG_KEYS2["OCO_OLLAMA_API_URL"] = "OCO_OLLAMA_API_URL";
CONFIG_KEYS2["OCO_FLOWISE_ENDPOINT"] = "OCO_FLOWISE_ENDPOINT";
CONFIG_KEYS2["OCO_FLOWISE_API_KEY"] = "OCO_FLOWISE_API_KEY";
return CONFIG_KEYS2;
})(CONFIG_KEYS || {});
var MODEL_LIST = { var MODEL_LIST = {
openai: [ openai: [
"gpt-4o-mini", "gpt-4o-mini",
@@ -48781,56 +48754,18 @@ var validateConfig = (key, condition, validationMessage) => {
} }
}; };
var configValidators = { var configValidators = {
["OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */](value, config6 = {}) { ["OCO_API_KEY" /* OCO_API_KEY */](value, config6 = {}) {
if (config6.OCO_AI_PROVIDER !== "openai") if (config6.OCO_AI_PROVIDER !== "openai")
return value; return value;
validateConfig( validateConfig(
"OCO_OPENAI_API_KEY", "OCO_API_KEY",
typeof value === "string" && value.length > 0, typeof value === "string" && value.length > 0,
"Empty value is not allowed" "Empty value is not allowed"
); );
validateConfig( validateConfig(
"OCO_OPENAI_API_KEY", "OCO_API_KEY",
value, value,
'You need to provide the OCO_OPENAI_API_KEY when OCO_AI_PROVIDER is set to "openai" (default). Run `oco config set OCO_OPENAI_API_KEY=your_key`' 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`'
);
return value;
},
["OCO_AZURE_API_KEY" /* OCO_AZURE_API_KEY */](value, config6 = {}) {
if (config6.OCO_AI_PROVIDER !== "azure")
return value;
validateConfig(
"OCO_AZURE_API_KEY",
!!value,
'You need to provide the OCO_AZURE_API_KEY when OCO_AI_PROVIDER is set to "azure". Run: `oco config set OCO_AZURE_API_KEY=your_key`'
);
return value;
},
["OCO_GEMINI_API_KEY" /* OCO_GEMINI_API_KEY */](value, config6 = {}) {
if (config6.OCO_AI_PROVIDER !== "gemini")
return value;
validateConfig(
"OCO_GEMINI_API_KEY",
value || config6.OCO_GEMINI_API_KEY || config6.OCO_AI_PROVIDER === "test",
'You need to provide the OCO_GEMINI_API_KEY when OCO_AI_PROVIDER is set to "gemini". Run: `oco config set OCO_GEMINI_API_KEY=your_key`'
);
return value;
},
["OCO_ANTHROPIC_API_KEY" /* OCO_ANTHROPIC_API_KEY */](value, config6 = {}) {
if (config6.OCO_AI_PROVIDER !== "anthropic")
return value;
validateConfig(
"ANTHROPIC_API_KEY",
!!value,
'You need to provide the OCO_ANTHROPIC_API_KEY key when OCO_AI_PROVIDER is set to "anthropic". Run: `oco config set OCO_ANTHROPIC_API_KEY=your_key`'
);
return value;
},
["OCO_FLOWISE_API_KEY" /* OCO_FLOWISE_API_KEY */](value, config6 = {}) {
validateConfig(
"OCO_FLOWISE_API_KEY" /* OCO_FLOWISE_API_KEY */,
value || config6.OCO_AI_PROVIDER !== "flowise",
'You need to provide the OCO_FLOWISE_API_KEY when OCO_AI_PROVIDER is set to "flowise". Run: `oco config set OCO_FLOWISE_API_KEY=your_key`'
); );
return value; return value;
}, },
@@ -48877,11 +48812,11 @@ var configValidators = {
); );
return getI18nLocal(value); return getI18nLocal(value);
}, },
["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */](value) { ["OCO_API_URL" /* OCO_API_URL */](value) {
validateConfig( validateConfig(
"OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */, "OCO_API_URL" /* OCO_API_URL */,
typeof value === "string", typeof value === "string",
"Must be string" `${value} is not a valid URL. It should start with 'http://' or 'https://'.`
); );
return value; return value;
}, },
@@ -48943,22 +48878,6 @@ var configValidators = {
); );
return value; return value;
}, },
["OCO_AZURE_ENDPOINT" /* OCO_AZURE_ENDPOINT */](value) {
validateConfig(
"OCO_AZURE_ENDPOINT" /* OCO_AZURE_ENDPOINT */,
value.includes("openai.azure.com"),
'Must be in format "https://<resource name>.openai.azure.com/"'
);
return value;
},
["OCO_FLOWISE_ENDPOINT" /* OCO_FLOWISE_ENDPOINT */](value) {
validateConfig(
"OCO_FLOWISE_ENDPOINT" /* OCO_FLOWISE_ENDPOINT */,
typeof value === "string" && value.includes(":"),
"Value must be string and should include both I.P. and port number"
);
return value;
},
["OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */](value) { ["OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */](value) {
validateConfig( validateConfig(
"OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */, "OCO_TEST_MOCK_TYPE" /* OCO_TEST_MOCK_TYPE */,
@@ -48969,11 +48888,11 @@ var configValidators = {
); );
return value; return value;
}, },
["OCO_OLLAMA_API_URL" /* OCO_OLLAMA_API_URL */](value) { ["OCO_WHY" /* OCO_WHY */](value) {
validateConfig( validateConfig(
"OCO_OLLAMA_API_URL" /* OCO_OLLAMA_API_URL */, "OCO_WHY" /* OCO_WHY */,
typeof value === "string" && value.startsWith("http"), typeof value === "boolean",
`${value} is not a valid URL. It should start with 'http://' or 'https://'.` "Must be true or false"
); );
return value; return value;
} }
@@ -49000,7 +48919,7 @@ var initGlobalConfig = (configPath = defaultConfigPath) => {
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8"); (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8");
return DEFAULT_CONFIG; return DEFAULT_CONFIG;
}; };
var parseEnvVarValue = (value) => { var parseConfigVarValue = (value) => {
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch (error) { } catch (error) {
@@ -49011,32 +48930,32 @@ var getEnvConfig = (envPath) => {
dotenv.config({ path: envPath }); dotenv.config({ path: envPath });
return { return {
OCO_MODEL: process.env.OCO_MODEL, OCO_MODEL: process.env.OCO_MODEL,
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, OCO_API_URL: process.env.OCO_API_URL,
OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY, OCO_API_KEY: process.env.OCO_API_KEY,
OCO_AZURE_API_KEY: process.env.OCO_AZURE_API_KEY, OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER,
OCO_GEMINI_API_KEY: process.env.OCO_GEMINI_API_KEY, OCO_TOKENS_MAX_INPUT: parseConfigVarValue(process.env.OCO_TOKENS_MAX_INPUT),
OCO_FLOWISE_API_KEY: process.env.OCO_FLOWISE_API_KEY, OCO_TOKENS_MAX_OUTPUT: parseConfigVarValue(
OCO_TOKENS_MAX_INPUT: parseEnvVarValue(process.env.OCO_TOKENS_MAX_INPUT), process.env.OCO_TOKENS_MAX_OUTPUT
OCO_TOKENS_MAX_OUTPUT: parseEnvVarValue(process.env.OCO_TOKENS_MAX_OUTPUT), ),
OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, OCO_DESCRIPTION: parseConfigVarValue(process.env.OCO_DESCRIPTION),
OCO_GEMINI_BASE_PATH: process.env.OCO_GEMINI_BASE_PATH, OCO_EMOJI: parseConfigVarValue(process.env.OCO_EMOJI),
OCO_AZURE_ENDPOINT: process.env.OCO_AZURE_ENDPOINT,
OCO_FLOWISE_ENDPOINT: process.env.OCO_FLOWISE_ENDPOINT,
OCO_OLLAMA_API_URL: process.env.OCO_OLLAMA_API_URL,
OCO_DESCRIPTION: parseEnvVarValue(process.env.OCO_DESCRIPTION),
OCO_EMOJI: parseEnvVarValue(process.env.OCO_EMOJI),
OCO_LANGUAGE: process.env.OCO_LANGUAGE, OCO_LANGUAGE: process.env.OCO_LANGUAGE,
OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER, OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER,
OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE, OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE,
OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER, OCO_ONE_LINE_COMMIT: parseConfigVarValue(process.env.OCO_ONE_LINE_COMMIT),
OCO_ONE_LINE_COMMIT: parseEnvVarValue(process.env.OCO_ONE_LINE_COMMIT),
OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE, OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE,
OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) OCO_GITPUSH: parseConfigVarValue(process.env.OCO_GITPUSH)
}; };
}; };
var getGlobalConfig = (configPath) => { var setGlobalConfig = (config6, configPath = defaultConfigPath) => {
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config6), "utf8");
};
var getIsGlobalConfigFileExist = (configPath = defaultConfigPath) => {
return (0, import_fs.existsSync)(configPath);
};
var getGlobalConfig = (configPath = defaultConfigPath) => {
let globalConfig; let globalConfig;
const isGlobalConfigFileExist = (0, import_fs.existsSync)(configPath); const isGlobalConfigFileExist = getIsGlobalConfigFileExist(configPath);
if (!isGlobalConfigFileExist) if (!isGlobalConfigFileExist)
globalConfig = initGlobalConfig(configPath); globalConfig = initGlobalConfig(configPath);
else { else {
@@ -49045,10 +48964,13 @@ var getGlobalConfig = (configPath) => {
} }
return globalConfig; return globalConfig;
}; };
var mergeConfigs = (main, fallback) => Object.keys(CONFIG_KEYS).reduce((acc, key) => { var mergeConfigs = (main, fallback) => {
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]); const allKeys = /* @__PURE__ */ new Set([...Object.keys(main), ...Object.keys(fallback)]);
return acc; return Array.from(allKeys).reduce((acc, key) => {
}, {}); acc[key] = parseConfigVarValue(main[key] ?? fallback[key]);
return acc;
}, {});
};
var getConfig = ({ var getConfig = ({
envPath = defaultEnvPath, envPath = defaultEnvPath,
globalPath = defaultConfigPath globalPath = defaultConfigPath
@@ -49062,6 +48984,7 @@ var setConfig = (keyValues, globalConfigPath = defaultConfigPath) => {
const config6 = getConfig({ const config6 = getConfig({
globalPath: globalConfigPath globalPath: globalConfigPath
}); });
const configToSet = {};
for (let [key, value] of keyValues) { for (let [key, value] of keyValues) {
if (!configValidators.hasOwnProperty(key)) { if (!configValidators.hasOwnProperty(key)) {
const supportedKeys = Object.keys(configValidators).join("\n"); const supportedKeys = Object.keys(configValidators).join("\n");
@@ -49075,7 +48998,10 @@ For more help refer to our docs: https://github.com/di-sukharev/opencommit`
} }
let parsedConfigValue; let parsedConfigValue;
try { try {
parsedConfigValue = JSON.parse(value); if (typeof value === "string")
parsedConfigValue = JSON.parse(value);
else
parsedConfigValue = value;
} catch (error) { } catch (error) {
parsedConfigValue = value; parsedConfigValue = value;
} }
@@ -49083,9 +49009,9 @@ For more help refer to our docs: https://github.com/di-sukharev/opencommit`
parsedConfigValue, parsedConfigValue,
config6 config6
); );
config6[key] = validValue; configToSet[key] = validValue;
} }
(0, import_fs.writeFileSync)(globalConfigPath, (0, import_ini.stringify)(config6), "utf8"); setGlobalConfig(mergeConfigs(configToSet, config6), globalConfigPath);
ce(`${source_default.green("\u2714")} config successfully set`); ce(`${source_default.green("\u2714")} config successfully set`);
}; };
var configCommand = G2( var configCommand = G2(
@@ -58171,7 +58097,7 @@ var AzureEngine = class {
}; };
// src/engine/flowise.ts // src/engine/flowise.ts
var FlowiseAi = class { var FlowiseEngine = class {
constructor(config6) { constructor(config6) {
this.config = config6; this.config = config6;
this.client = axios_default.create({ this.client = axios_default.create({
@@ -58966,7 +58892,7 @@ var GoogleGenerativeAI = class {
}; };
// src/engine/gemini.ts // src/engine/gemini.ts
var Gemini = class { var GeminiEngine = class {
constructor(config6) { constructor(config6) {
this.client = new GoogleGenerativeAI(config6.apiKey); this.client = new GoogleGenerativeAI(config6.apiKey);
this.config = config6; this.config = config6;
@@ -59024,7 +58950,7 @@ var Gemini = class {
}; };
// src/engine/ollama.ts // src/engine/ollama.ts
var OllamaAi = class { var OllamaEngine = class {
constructor(config6) { constructor(config6) {
this.config = config6; this.config = config6;
this.client = axios_default.create({ this.client = axios_default.create({
@@ -63359,44 +63285,24 @@ function getEngine() {
model: config6.OCO_MODEL, model: config6.OCO_MODEL,
maxTokensOutput: config6.OCO_TOKENS_MAX_OUTPUT, maxTokensOutput: config6.OCO_TOKENS_MAX_OUTPUT,
maxTokensInput: config6.OCO_TOKENS_MAX_INPUT, maxTokensInput: config6.OCO_TOKENS_MAX_INPUT,
baseURL: config6.OCO_OPENAI_BASE_PATH baseURL: config6.OCO_API_URL,
apiKey: config6.OCO_API_KEY
}; };
switch (provider) { switch (provider) {
case "ollama" /* OLLAMA */: case "ollama" /* OLLAMA */:
return new OllamaAi({ return new OllamaEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: "",
baseURL: config6.OCO_OLLAMA_API_URL
});
case "anthropic" /* ANTHROPIC */: case "anthropic" /* ANTHROPIC */:
return new AnthropicEngine({ return new AnthropicEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config6.OCO_ANTHROPIC_API_KEY
});
case "test" /* TEST */: case "test" /* TEST */:
return new TestAi(config6.OCO_TEST_MOCK_TYPE); return new TestAi(config6.OCO_TEST_MOCK_TYPE);
case "gemini" /* GEMINI */: case "gemini" /* GEMINI */:
return new Gemini({ return new GeminiEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config6.OCO_GEMINI_API_KEY,
baseURL: config6.OCO_GEMINI_BASE_PATH
});
case "azure" /* AZURE */: case "azure" /* AZURE */:
return new AzureEngine({ return new AzureEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config6.OCO_AZURE_API_KEY
});
case "flowise" /* FLOWISE */: case "flowise" /* FLOWISE */:
return new FlowiseAi({ return new FlowiseEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
baseURL: config6.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG2.baseURL,
apiKey: config6.OCO_FLOWISE_API_KEY
});
default: default:
return new OpenAiEngine({ return new OpenAiEngine(DEFAULT_CONFIG2);
...DEFAULT_CONFIG2,
apiKey: config6.OCO_OPENAI_API_KEY
});
} }
} }
@@ -63880,8 +63786,8 @@ function mergeDiffs(arr, maxStringLength) {
// src/generateCommitMessageFromGitDiff.ts // src/generateCommitMessageFromGitDiff.ts
var config5 = getConfig(); var config5 = getConfig();
var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT || 40960 /* DEFAULT_MAX_TOKENS_INPUT */; var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT;
var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT || 4096 /* DEFAULT_MAX_TOKENS_OUTPUT */; var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT;
var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec) => { var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec) => {
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec);
const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT]; const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT];

59
package-lock.json generated
View File

@@ -28,7 +28,7 @@
"ignore": "^5.2.4", "ignore": "^5.2.4",
"ini": "^3.0.1", "ini": "^3.0.1",
"inquirer": "^9.1.4", "inquirer": "^9.1.4",
"openai": "^4.56.0" "openai": "^4.57.0"
}, },
"bin": { "bin": {
"oco": "out/cli.cjs", "oco": "out/cli.cjs",
@@ -2098,6 +2098,11 @@
"form-data": "^4.0.0" "form-data": "^4.0.0"
} }
}, },
"node_modules/@types/qs": {
"version": "6.9.15",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
"integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg=="
},
"node_modules/@types/semver": { "node_modules/@types/semver": {
"version": "7.5.8", "version": "7.5.8",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
@@ -7219,6 +7224,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/object-inspect": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -7242,17 +7258,19 @@
} }
}, },
"node_modules/openai": { "node_modules/openai": {
"version": "4.56.0", "version": "4.57.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.56.0.tgz", "resolved": "https://registry.npmjs.org/openai/-/openai-4.57.0.tgz",
"integrity": "sha512-zcag97+3bG890MNNa0DQD9dGmmTWL8unJdNkulZzWRXrl+QeD+YkBI4H58rJcwErxqGK6a0jVPZ4ReJjhDGcmw==", "integrity": "sha512-JnwBSIYqiZ3jYjB5f2in8hQ0PRA092c6m+/6dYB0MzK0BEbn+0dioxZsPLBm5idJbg9xzLNOiGVm2OSuhZ+BdQ==",
"dependencies": { "dependencies": {
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4", "@types/node-fetch": "^2.6.4",
"@types/qs": "^6.9.7",
"abort-controller": "^3.0.0", "abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1", "agentkeepalive": "^4.2.1",
"form-data-encoder": "1.7.2", "form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2", "formdata-node": "^4.3.2",
"node-fetch": "^2.6.7" "node-fetch": "^2.6.7",
"qs": "^6.10.3"
}, },
"bin": { "bin": {
"openai": "bin/cli" "openai": "bin/cli"
@@ -7704,6 +7722,20 @@
} }
] ]
}, },
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -8050,6 +8082,23 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/signal-exit": { "node_modules/signal-exit": {
"version": "3.0.7", "version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",

View File

@@ -88,7 +88,6 @@
"@google/generative-ai": "^0.11.4", "@google/generative-ai": "^0.11.4",
"@octokit/webhooks-schemas": "^6.11.0", "@octokit/webhooks-schemas": "^6.11.0",
"@octokit/webhooks-types": "^6.11.0", "@octokit/webhooks-types": "^6.11.0",
"ai": "^2.2.14",
"axios": "^1.3.4", "axios": "^1.3.4",
"chalk": "^5.2.0", "chalk": "^5.2.0",
"cleye": "^1.3.2", "cleye": "^1.3.2",
@@ -97,6 +96,6 @@
"ignore": "^5.2.4", "ignore": "^5.2.4",
"ini": "^3.0.1", "ini": "^3.0.1",
"inquirer": "^9.1.4", "inquirer": "^9.1.4",
"openai": "^4.56.0" "openai": "^4.57.0"
} }
} }

View File

@@ -9,6 +9,7 @@ import { configCommand } from './commands/config';
import { hookCommand, isHookCalled } from './commands/githook.js'; import { hookCommand, isHookCalled } from './commands/githook.js';
import { prepareCommitMessageHook } from './commands/prepare-commit-msg-hook'; import { prepareCommitMessageHook } from './commands/prepare-commit-msg-hook';
import { checkIsLatestVersion } from './utils/checkIsLatestVersion'; import { checkIsLatestVersion } from './utils/checkIsLatestVersion';
import { runMigrations } from './migrations/_run.js';
const extraArgs = process.argv.slice(2); const extraArgs = process.argv.slice(2);
@@ -30,6 +31,7 @@ cli(
help: { description: packageJSON.description } help: { description: packageJSON.description }
}, },
async ({ flags }) => { async ({ flags }) => {
await runMigrations();
await checkIsLatestVersion(); await checkIsLatestVersion();
if (await isHookCalled()) { if (await isHookCalled()) {

View File

@@ -159,13 +159,13 @@ ${chalk.grey('——————————————————')}`
const { stdout } = await execa('git', ['push', selectedRemote]); const { stdout } = await execa('git', ['push', selectedRemote]);
if (stdout) outro(stdout);
pushSpinner.stop( pushSpinner.stop(
`${chalk.green( `${chalk.green(
'✔' '✔'
)} Successfully pushed all commits to ${selectedRemote}` )} successfully pushed all commits to ${selectedRemote}`
); );
if (stdout) outro(stdout);
} }
} else { } else {
const regenerateMessage = await confirm({ const regenerateMessage = await confirm({

View File

@@ -11,14 +11,9 @@ import { TEST_MOCK_TYPES } from '../engine/testAi';
import { getI18nLocal, i18n } from '../i18n'; import { getI18nLocal, i18n } from '../i18n';
export enum CONFIG_KEYS { export enum CONFIG_KEYS {
OCO_OPENAI_API_KEY = 'OCO_OPENAI_API_KEY', OCO_API_KEY = 'OCO_API_KEY',
OCO_ANTHROPIC_API_KEY = 'OCO_ANTHROPIC_API_KEY',
OCO_AZURE_API_KEY = 'OCO_AZURE_API_KEY',
OCO_GEMINI_API_KEY = 'OCO_GEMINI_API_KEY',
OCO_GEMINI_BASE_PATH = 'OCO_GEMINI_BASE_PATH',
OCO_TOKENS_MAX_INPUT = 'OCO_TOKENS_MAX_INPUT', OCO_TOKENS_MAX_INPUT = 'OCO_TOKENS_MAX_INPUT',
OCO_TOKENS_MAX_OUTPUT = 'OCO_TOKENS_MAX_OUTPUT', OCO_TOKENS_MAX_OUTPUT = 'OCO_TOKENS_MAX_OUTPUT',
OCO_OPENAI_BASE_PATH = 'OCO_OPENAI_BASE_PATH',
OCO_DESCRIPTION = 'OCO_DESCRIPTION', OCO_DESCRIPTION = 'OCO_DESCRIPTION',
OCO_EMOJI = 'OCO_EMOJI', OCO_EMOJI = 'OCO_EMOJI',
OCO_MODEL = 'OCO_MODEL', OCO_MODEL = 'OCO_MODEL',
@@ -27,14 +22,10 @@ export enum CONFIG_KEYS {
OCO_MESSAGE_TEMPLATE_PLACEHOLDER = 'OCO_MESSAGE_TEMPLATE_PLACEHOLDER', OCO_MESSAGE_TEMPLATE_PLACEHOLDER = 'OCO_MESSAGE_TEMPLATE_PLACEHOLDER',
OCO_PROMPT_MODULE = 'OCO_PROMPT_MODULE', OCO_PROMPT_MODULE = 'OCO_PROMPT_MODULE',
OCO_AI_PROVIDER = 'OCO_AI_PROVIDER', OCO_AI_PROVIDER = 'OCO_AI_PROVIDER',
OCO_GITPUSH = 'OCO_GITPUSH', // todo: deprecate
OCO_ONE_LINE_COMMIT = 'OCO_ONE_LINE_COMMIT', OCO_ONE_LINE_COMMIT = 'OCO_ONE_LINE_COMMIT',
OCO_AZURE_ENDPOINT = 'OCO_AZURE_ENDPOINT',
OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE', OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE',
OCO_API_URL = 'OCO_API_URL', OCO_API_URL = 'OCO_API_URL',
OCO_OLLAMA_API_URL = 'OCO_OLLAMA_API_URL', OCO_GITPUSH = 'OCO_GITPUSH' // todo: deprecate
OCO_FLOWISE_ENDPOINT = 'OCO_FLOWISE_ENDPOINT',
OCO_FLOWISE_API_KEY = 'OCO_FLOWISE_API_KEY'
} }
export enum CONFIG_MODES { export enum CONFIG_MODES {
@@ -123,65 +114,19 @@ const validateConfig = (
}; };
export const configValidators = { export const configValidators = {
[CONFIG_KEYS.OCO_OPENAI_API_KEY](value: any, config: any = {}) { [CONFIG_KEYS.OCO_API_KEY](value: any, config: any = {}) {
if (config.OCO_AI_PROVIDER !== 'openai') return value; if (config.OCO_AI_PROVIDER !== 'openai') return value;
validateConfig( validateConfig(
'OCO_OPENAI_API_KEY', 'OCO_API_KEY',
typeof value === 'string' && value.length > 0, typeof value === 'string' && value.length > 0,
'Empty value is not allowed' 'Empty value is not allowed'
); );
validateConfig( validateConfig(
'OCO_OPENAI_API_KEY', 'OCO_API_KEY',
value, value,
'You need to provide the OCO_OPENAI_API_KEY when OCO_AI_PROVIDER is set to "openai" (default). Run `oco config set OCO_OPENAI_API_KEY=your_key`' 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`'
);
return value;
},
[CONFIG_KEYS.OCO_AZURE_API_KEY](value: any, config: any = {}) {
if (config.OCO_AI_PROVIDER !== 'azure') return value;
validateConfig(
'OCO_AZURE_API_KEY',
!!value,
'You need to provide the OCO_AZURE_API_KEY when OCO_AI_PROVIDER is set to "azure". Run: `oco config set OCO_AZURE_API_KEY=your_key`'
);
return value;
},
[CONFIG_KEYS.OCO_GEMINI_API_KEY](value: any, config: any = {}) {
if (config.OCO_AI_PROVIDER !== 'gemini') return value;
validateConfig(
'OCO_GEMINI_API_KEY',
value || config.OCO_GEMINI_API_KEY || config.OCO_AI_PROVIDER === 'test',
'You need to provide the OCO_GEMINI_API_KEY when OCO_AI_PROVIDER is set to "gemini". Run: `oco config set OCO_GEMINI_API_KEY=your_key`'
);
return value;
},
[CONFIG_KEYS.OCO_ANTHROPIC_API_KEY](value: any, config: any = {}) {
if (config.OCO_AI_PROVIDER !== 'anthropic') return value;
validateConfig(
'ANTHROPIC_API_KEY',
!!value,
'You need to provide the OCO_ANTHROPIC_API_KEY key when OCO_AI_PROVIDER is set to "anthropic". Run: `oco config set OCO_ANTHROPIC_API_KEY=your_key`'
);
return value;
},
[CONFIG_KEYS.OCO_FLOWISE_API_KEY](value: any, config: any = {}) {
validateConfig(
CONFIG_KEYS.OCO_FLOWISE_API_KEY,
value || config.OCO_AI_PROVIDER !== 'flowise',
'You need to provide the OCO_FLOWISE_API_KEY when OCO_AI_PROVIDER is set to "flowise". Run: `oco config set OCO_FLOWISE_API_KEY=your_key`'
); );
return value; return value;
@@ -241,11 +186,11 @@ export const configValidators = {
return getI18nLocal(value); return getI18nLocal(value);
}, },
[CONFIG_KEYS.OCO_OPENAI_BASE_PATH](value: any) { [CONFIG_KEYS.OCO_API_URL](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_OPENAI_BASE_PATH, CONFIG_KEYS.OCO_API_URL,
typeof value === 'string', typeof value === 'string',
'Must be string' `${value} is not a valid URL. It should start with 'http://' or 'https://'.`
); );
return value; return value;
}, },
@@ -315,26 +260,6 @@ export const configValidators = {
return value; return value;
}, },
[CONFIG_KEYS.OCO_AZURE_ENDPOINT](value: any) {
validateConfig(
CONFIG_KEYS.OCO_AZURE_ENDPOINT,
value.includes('openai.azure.com'),
'Must be in format "https://<resource name>.openai.azure.com/"'
);
return value;
},
[CONFIG_KEYS.OCO_FLOWISE_ENDPOINT](value: any) {
validateConfig(
CONFIG_KEYS.OCO_FLOWISE_ENDPOINT,
typeof value === 'string' && value.includes(':'),
'Value must be string and should include both I.P. and port number' // Considering the possibility of DNS lookup or feeding the I.P. explicitly, there is no pattern to verify, except a column for the port number
);
return value;
},
[CONFIG_KEYS.OCO_TEST_MOCK_TYPE](value: any) { [CONFIG_KEYS.OCO_TEST_MOCK_TYPE](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_TEST_MOCK_TYPE, CONFIG_KEYS.OCO_TEST_MOCK_TYPE,
@@ -346,11 +271,11 @@ export const configValidators = {
return value; return value;
}, },
[CONFIG_KEYS.OCO_OLLAMA_API_URL](value: any) { [CONFIG_KEYS.OCO_WHY](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_OLLAMA_API_URL, CONFIG_KEYS.OCO_WHY,
typeof value === 'string' && value.startsWith('http'), typeof value === 'boolean',
`${value} is not a valid URL. It should start with 'http://' or 'https://'.` 'Must be true or false'
); );
return value; return value;
} }
@@ -367,14 +292,10 @@ export enum OCO_AI_PROVIDER_ENUM {
} }
export type ConfigType = { export type ConfigType = {
[CONFIG_KEYS.OCO_OPENAI_API_KEY]?: string; [CONFIG_KEYS.OCO_API_KEY]?: string;
[CONFIG_KEYS.OCO_ANTHROPIC_API_KEY]?: string;
[CONFIG_KEYS.OCO_AZURE_API_KEY]?: string;
[CONFIG_KEYS.OCO_GEMINI_API_KEY]?: string;
[CONFIG_KEYS.OCO_GEMINI_BASE_PATH]?: string;
[CONFIG_KEYS.OCO_TOKENS_MAX_INPUT]: number; [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT]: number;
[CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT]: number; [CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT]: number;
[CONFIG_KEYS.OCO_OPENAI_BASE_PATH]?: string; [CONFIG_KEYS.OCO_API_URL]?: string;
[CONFIG_KEYS.OCO_DESCRIPTION]: boolean; [CONFIG_KEYS.OCO_DESCRIPTION]: boolean;
[CONFIG_KEYS.OCO_EMOJI]: boolean; [CONFIG_KEYS.OCO_EMOJI]: boolean;
[CONFIG_KEYS.OCO_WHY]: boolean; [CONFIG_KEYS.OCO_WHY]: boolean;
@@ -385,16 +306,11 @@ export type ConfigType = {
[CONFIG_KEYS.OCO_AI_PROVIDER]: OCO_AI_PROVIDER_ENUM; [CONFIG_KEYS.OCO_AI_PROVIDER]: OCO_AI_PROVIDER_ENUM;
[CONFIG_KEYS.OCO_GITPUSH]: boolean; [CONFIG_KEYS.OCO_GITPUSH]: boolean;
[CONFIG_KEYS.OCO_ONE_LINE_COMMIT]: boolean; [CONFIG_KEYS.OCO_ONE_LINE_COMMIT]: boolean;
[CONFIG_KEYS.OCO_AZURE_ENDPOINT]?: string;
[CONFIG_KEYS.OCO_TEST_MOCK_TYPE]: string; [CONFIG_KEYS.OCO_TEST_MOCK_TYPE]: string;
[CONFIG_KEYS.OCO_API_URL]?: string;
[CONFIG_KEYS.OCO_OLLAMA_API_URL]?: string;
[CONFIG_KEYS.OCO_FLOWISE_ENDPOINT]: string;
[CONFIG_KEYS.OCO_FLOWISE_API_KEY]?: string;
}; };
const defaultConfigPath = pathJoin(homedir(), '.opencommit'); export const defaultConfigPath = pathJoin(homedir(), '.opencommit');
const defaultEnvPath = pathResolve(process.cwd(), '.env'); export const defaultEnvPath = pathResolve(process.cwd(), '.env');
const assertConfigsAreValid = (config: Record<string, any>) => { const assertConfigsAreValid = (config: Record<string, any>) => {
for (const [key, value] of Object.entries(config)) { for (const [key, value] of Object.entries(config)) {
@@ -446,7 +362,7 @@ const initGlobalConfig = (configPath: string = defaultConfigPath) => {
return DEFAULT_CONFIG; return DEFAULT_CONFIG;
}; };
const parseEnvVarValue = (value?: any) => { const parseConfigVarValue = (value?: any) => {
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch (error) { } catch (error) {
@@ -459,41 +375,45 @@ const getEnvConfig = (envPath: string) => {
return { return {
OCO_MODEL: process.env.OCO_MODEL, OCO_MODEL: process.env.OCO_MODEL,
OCO_API_URL: process.env.OCO_API_URL,
OCO_API_KEY: process.env.OCO_API_KEY,
OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER as OCO_AI_PROVIDER_ENUM,
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, OCO_TOKENS_MAX_INPUT: parseConfigVarValue(process.env.OCO_TOKENS_MAX_INPUT),
OCO_ANTHROPIC_API_KEY: process.env.OCO_ANTHROPIC_API_KEY, OCO_TOKENS_MAX_OUTPUT: parseConfigVarValue(
OCO_AZURE_API_KEY: process.env.OCO_AZURE_API_KEY, process.env.OCO_TOKENS_MAX_OUTPUT
OCO_GEMINI_API_KEY: process.env.OCO_GEMINI_API_KEY, ),
OCO_FLOWISE_API_KEY: process.env.OCO_FLOWISE_API_KEY,
OCO_TOKENS_MAX_INPUT: parseEnvVarValue(process.env.OCO_TOKENS_MAX_INPUT), OCO_DESCRIPTION: parseConfigVarValue(process.env.OCO_DESCRIPTION),
OCO_TOKENS_MAX_OUTPUT: parseEnvVarValue(process.env.OCO_TOKENS_MAX_OUTPUT), OCO_EMOJI: parseConfigVarValue(process.env.OCO_EMOJI),
OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH,
OCO_GEMINI_BASE_PATH: process.env.OCO_GEMINI_BASE_PATH,
OCO_AZURE_ENDPOINT: process.env.OCO_AZURE_ENDPOINT,
OCO_FLOWISE_ENDPOINT: process.env.OCO_FLOWISE_ENDPOINT,
OCO_OLLAMA_API_URL: process.env.OCO_OLLAMA_API_URL,
OCO_DESCRIPTION: parseEnvVarValue(process.env.OCO_DESCRIPTION),
OCO_EMOJI: parseEnvVarValue(process.env.OCO_EMOJI),
OCO_LANGUAGE: process.env.OCO_LANGUAGE, OCO_LANGUAGE: process.env.OCO_LANGUAGE,
OCO_MESSAGE_TEMPLATE_PLACEHOLDER: OCO_MESSAGE_TEMPLATE_PLACEHOLDER:
process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER, process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER,
OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE as OCO_PROMPT_MODULE_ENUM, OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE as OCO_PROMPT_MODULE_ENUM,
OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER as OCO_AI_PROVIDER_ENUM, OCO_ONE_LINE_COMMIT: parseConfigVarValue(process.env.OCO_ONE_LINE_COMMIT),
OCO_ONE_LINE_COMMIT: parseEnvVarValue(process.env.OCO_ONE_LINE_COMMIT),
OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE, OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE,
OCO_GITPUSH: parseEnvVarValue(process.env.OCO_GITPUSH) // todo: deprecate OCO_GITPUSH: parseConfigVarValue(process.env.OCO_GITPUSH) // todo: deprecate
}; };
}; };
const getGlobalConfig = (configPath: string) => { export const setGlobalConfig = (
config: ConfigType,
configPath: string = defaultConfigPath
) => {
writeFileSync(configPath, iniStringify(config), 'utf8');
};
export const getIsGlobalConfigFileExist = (
configPath: string = defaultConfigPath
) => {
return existsSync(configPath);
};
export const getGlobalConfig = (configPath: string = defaultConfigPath) => {
let globalConfig: ConfigType; let globalConfig: ConfigType;
const isGlobalConfigFileExist = existsSync(configPath); const isGlobalConfigFileExist = getIsGlobalConfigFileExist(configPath);
if (!isGlobalConfigFileExist) globalConfig = initGlobalConfig(configPath); if (!isGlobalConfigFileExist) globalConfig = initGlobalConfig(configPath);
else { else {
const configFile = readFileSync(configPath, 'utf8'); const configFile = readFileSync(configPath, 'utf8');
@@ -510,16 +430,18 @@ const getGlobalConfig = (configPath: string) => {
* @param fallback - global ~/.opencommit config file * @param fallback - global ~/.opencommit config file
* @returns merged config * @returns merged config
*/ */
const mergeConfigs = (main: Partial<ConfigType>, fallback: ConfigType) => const mergeConfigs = (main: Partial<ConfigType>, fallback: ConfigType) => {
Object.keys(CONFIG_KEYS).reduce((acc, key) => { const allKeys = new Set([...Object.keys(main), ...Object.keys(fallback)]);
acc[key] = parseEnvVarValue(main[key] ?? fallback[key]); return Array.from(allKeys).reduce((acc, key) => {
acc[key] = parseConfigVarValue(main[key] ?? fallback[key]);
return acc; return acc;
}, {} as ConfigType); }, {} as ConfigType);
};
interface GetConfigOptions { interface GetConfigOptions {
globalPath?: string; globalPath?: string;
envPath?: string; envPath?: string;
setDefaultValues?: boolean;
} }
export const getConfig = ({ export const getConfig = ({
@@ -535,13 +457,15 @@ export const getConfig = ({
}; };
export const setConfig = ( export const setConfig = (
keyValues: [key: string, value: string][], keyValues: [key: string, value: string | boolean | number | null][],
globalConfigPath: string = defaultConfigPath globalConfigPath: string = defaultConfigPath
) => { ) => {
const config = getConfig({ const config = getConfig({
globalPath: globalConfigPath globalPath: globalConfigPath
}); });
const configToSet = {};
for (let [key, value] of keyValues) { for (let [key, value] of keyValues) {
if (!configValidators.hasOwnProperty(key)) { if (!configValidators.hasOwnProperty(key)) {
const supportedKeys = Object.keys(configValidators).join('\n'); const supportedKeys = Object.keys(configValidators).join('\n');
@@ -553,7 +477,8 @@ export const setConfig = (
let parsedConfigValue; let parsedConfigValue;
try { try {
parsedConfigValue = JSON.parse(value); if (typeof value === 'string') parsedConfigValue = JSON.parse(value);
else parsedConfigValue = value;
} catch (error) { } catch (error) {
parsedConfigValue = value; parsedConfigValue = value;
} }
@@ -563,10 +488,10 @@ export const setConfig = (
config config
); );
config[key] = validValue; configToSet[key] = validValue;
} }
writeFileSync(globalConfigPath, iniStringify(config), 'utf8'); setGlobalConfig(mergeConfigs(configToSet, config), globalConfigPath);
outro(`${chalk.green('✔')} config successfully set`); outro(`${chalk.green('✔')} config successfully set`);
}; };

View File

@@ -39,13 +39,9 @@ export const prepareCommitMessageHook = async (
const config = getConfig(); const config = getConfig();
if ( if (!config.OCO_API_KEY) {
!config.OCO_OPENAI_API_KEY &&
!config.OCO_ANTHROPIC_API_KEY &&
!config.OCO_AZURE_API_KEY
) {
outro( outro(
'No OCO_OPENAI_API_KEY or OCO_ANTHROPIC_API_KEY or OCO_AZURE_API_KEY exists. Set your key via `oco config set <key>=<value>, e.g. `oco config set OCO_OPENAI_API_KEY=<value>`. For more info see https://github.com/di-sukharev/opencommit' 'No OCO_API_KEY is set. Set your key via `oco config set OCO_API_KEY=<value>. For more info see https://github.com/di-sukharev/opencommit'
); );
return; return;
} }

View File

@@ -4,7 +4,7 @@ import { AiEngine, AiEngineConfig } from './Engine';
interface FlowiseAiConfig extends AiEngineConfig {} interface FlowiseAiConfig extends AiEngineConfig {}
export class FlowiseAi implements AiEngine { export class FlowiseEngine implements AiEngine {
config: FlowiseAiConfig; config: FlowiseAiConfig;
client: AxiosInstance; client: AxiosInstance;

View File

@@ -11,7 +11,7 @@ import { AiEngine, AiEngineConfig } from './Engine';
interface GeminiConfig extends AiEngineConfig {} interface GeminiConfig extends AiEngineConfig {}
export class Gemini implements AiEngine { export class GeminiEngine implements AiEngine {
config: GeminiConfig; config: GeminiConfig;
client: GoogleGenerativeAI; client: GoogleGenerativeAI;

View File

@@ -4,7 +4,7 @@ import { AiEngine, AiEngineConfig } from './Engine';
interface OllamaConfig extends AiEngineConfig {} interface OllamaConfig extends AiEngineConfig {}
export class OllamaAi implements AiEngine { export class OllamaEngine implements AiEngine {
config: OllamaConfig; config: OllamaConfig;
client: AxiosInstance; client: AxiosInstance;

View File

@@ -6,11 +6,8 @@ import { mergeDiffs } from './utils/mergeDiffs';
import { tokenCount } from './utils/tokenCount'; import { tokenCount } from './utils/tokenCount';
const config = getConfig(); const config = getConfig();
const MAX_TOKENS_INPUT = const MAX_TOKENS_INPUT = config.OCO_TOKENS_MAX_INPUT;
config.OCO_TOKENS_MAX_INPUT || DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_INPUT; const MAX_TOKENS_OUTPUT = config.OCO_TOKENS_MAX_OUTPUT;
const MAX_TOKENS_OUTPUT =
config.OCO_TOKENS_MAX_OUTPUT ||
DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT;
const generateCommitMessageChatCompletionPrompt = async ( const generateCommitMessageChatCompletionPrompt = async (
diff: string, diff: string,

View File

@@ -0,0 +1,45 @@
import {
CONFIG_KEYS,
getConfig,
OCO_AI_PROVIDER_ENUM,
setConfig
} from '../commands/config';
export default function () {
const config = getConfig({ setDefaultValues: false });
const aiProvider = config.OCO_AI_PROVIDER;
let apiKey: string | undefined;
let apiUrl: string | undefined;
if (aiProvider === OCO_AI_PROVIDER_ENUM.OLLAMA) {
apiKey = config['OCO_OLLAMA_API_KEY'];
apiUrl = config['OCO_OLLAMA_API_URL'];
} else if (aiProvider === OCO_AI_PROVIDER_ENUM.ANTHROPIC) {
apiKey = config['OCO_ANTHROPIC_API_KEY'];
apiUrl = config['OCO_ANTHROPIC_BASE_PATH'];
} else if (aiProvider === OCO_AI_PROVIDER_ENUM.OPENAI) {
apiKey = config['OCO_OPENAI_API_KEY'];
apiUrl = config['OCO_OPENAI_BASE_PATH'];
} else if (aiProvider === OCO_AI_PROVIDER_ENUM.AZURE) {
apiKey = config['OCO_AZURE_API_KEY'];
apiUrl = config['OCO_AZURE_ENDPOINT'];
} else if (aiProvider === OCO_AI_PROVIDER_ENUM.GEMINI) {
apiKey = config['OCO_GEMINI_API_KEY'];
apiUrl = config['OCO_GEMINI_BASE_PATH'];
} else if (aiProvider === OCO_AI_PROVIDER_ENUM.FLOWISE) {
apiKey = config['OCO_FLOWISE_API_KEY'];
apiUrl = config['OCO_FLOWISE_ENDPOINT'];
} else {
throw new Error(
`Migration failed, set AI provider first. Run "oco config set OCO_AI_PROVIDER=<provider>", where <provider> is one of: ${Object.values(
OCO_AI_PROVIDER_ENUM
).join(', ')}`
);
}
if (apiKey) setConfig([[CONFIG_KEYS.OCO_API_KEY, apiKey]]);
if (apiUrl) setConfig([[CONFIG_KEYS.OCO_API_URL, apiUrl]]);
}

View File

@@ -0,0 +1,26 @@
import { getGlobalConfig, setGlobalConfig } from '../commands/config';
export default function () {
const obsoleteKeys = [
'OCO_OLLAMA_API_KEY',
'OCO_OLLAMA_API_URL',
'OCO_ANTHROPIC_API_KEY',
'OCO_ANTHROPIC_BASE_PATH',
'OCO_OPENAI_API_KEY',
'OCO_OPENAI_BASE_PATH',
'OCO_AZURE_API_KEY',
'OCO_AZURE_ENDPOINT',
'OCO_GEMINI_API_KEY',
'OCO_GEMINI_BASE_PATH',
'OCO_FLOWISE_API_KEY',
'OCO_FLOWISE_ENDPOINT'
];
const globalConfig = getGlobalConfig();
const configToOverride = { ...globalConfig };
for (const key of obsoleteKeys) delete configToOverride[key];
setGlobalConfig(configToOverride);
}

View File

@@ -0,0 +1,20 @@
import {
ConfigType,
DEFAULT_CONFIG,
getGlobalConfig,
setConfig
} from '../commands/config';
export default function () {
const setDefaultConfigValues = (config: ConfigType) => {
const entriesToSet: [key: string, value: string | boolean | number][] = [];
for (const entry of Object.entries(DEFAULT_CONFIG)) {
const [key, _value] = entry;
if (config[key] === 'undefined') entriesToSet.push(entry);
}
if (entriesToSet.length > 0) setConfig(entriesToSet);
};
setDefaultConfigValues(getGlobalConfig());
}

View File

@@ -0,0 +1,18 @@
import migration00 from './00_use_single_api_key_and_url';
import migration01 from './01_remove_obsolete_config_keys_from_global_file';
import migration02 from './02_set_missing_default_values';
export const migrations = [
{
name: '00_use_single_api_key_and_url',
run: migration00
},
{
name: '01_remove_obsolete_config_keys_from_global_file',
run: migration01
},
{
name: '02_set_missing_default_values',
run: migration02
}
];

70
src/migrations/_run.ts Normal file
View File

@@ -0,0 +1,70 @@
import fs from 'fs';
import { homedir } from 'os';
import { join as pathJoin } from 'path';
import { migrations } from './_migrations';
import { outro } from '@clack/prompts';
import chalk from 'chalk';
import {
getConfig,
getIsGlobalConfigFileExist,
OCO_AI_PROVIDER_ENUM
} from '../commands/config';
const migrationsFile = pathJoin(homedir(), '.opencommit_migrations');
const getCompletedMigrations = (): string[] => {
if (!fs.existsSync(migrationsFile)) {
return [];
}
const data = fs.readFileSync(migrationsFile, 'utf-8');
return data ? JSON.parse(data) : [];
};
const saveCompletedMigration = (migrationName: string) => {
const completedMigrations = getCompletedMigrations();
completedMigrations.push(migrationName);
fs.writeFileSync(
migrationsFile,
JSON.stringify(completedMigrations, null, 2)
);
};
export const runMigrations = async () => {
// if no config file, we assume it's a new installation and no migrations are needed
if (!getIsGlobalConfigFileExist()) return;
const config = getConfig();
if (config.OCO_AI_PROVIDER === OCO_AI_PROVIDER_ENUM.TEST) return;
const completedMigrations = getCompletedMigrations();
let isMigrated = false;
for (const migration of migrations) {
if (!completedMigrations.includes(migration.name)) {
try {
console.log('Applying migration', migration.name);
migration.run();
console.log('Migration applied successfully', migration.name);
saveCompletedMigration(migration.name);
} catch (error) {
outro(
`${chalk.red('Failed to apply migration')} ${
migration.name
}: ${error}`
);
}
isMigrated = true;
}
}
if (isMigrated) {
outro(
`${chalk.green(
'✔'
)} Migrations to your config were applied successfully. Please rerun.`
);
process.exit(0);
}
};

View File

@@ -2,9 +2,9 @@ import { getConfig, OCO_AI_PROVIDER_ENUM } from '../commands/config';
import { AnthropicEngine } from '../engine/anthropic'; import { AnthropicEngine } from '../engine/anthropic';
import { AzureEngine } from '../engine/azure'; import { AzureEngine } from '../engine/azure';
import { AiEngine } from '../engine/Engine'; import { AiEngine } from '../engine/Engine';
import { FlowiseAi } from '../engine/flowise'; import { FlowiseEngine } from '../engine/flowise';
import { Gemini } from '../engine/gemini'; import { GeminiEngine } from '../engine/gemini';
import { OllamaAi } from '../engine/ollama'; import { OllamaEngine } from '../engine/ollama';
import { OpenAiEngine } from '../engine/openAi'; import { OpenAiEngine } from '../engine/openAi';
import { TestAi, TestMockType } from '../engine/testAi'; import { TestAi, TestMockType } from '../engine/testAi';
@@ -16,50 +16,30 @@ export function getEngine(): AiEngine {
model: config.OCO_MODEL!, model: config.OCO_MODEL!,
maxTokensOutput: config.OCO_TOKENS_MAX_OUTPUT!, maxTokensOutput: config.OCO_TOKENS_MAX_OUTPUT!,
maxTokensInput: config.OCO_TOKENS_MAX_INPUT!, maxTokensInput: config.OCO_TOKENS_MAX_INPUT!,
baseURL: config.OCO_OPENAI_BASE_PATH! baseURL: config.OCO_API_URL!,
apiKey: config.OCO_API_KEY!
}; };
switch (provider) { switch (provider) {
case OCO_AI_PROVIDER_ENUM.OLLAMA: case OCO_AI_PROVIDER_ENUM.OLLAMA:
return new OllamaAi({ return new OllamaEngine(DEFAULT_CONFIG);
...DEFAULT_CONFIG,
apiKey: '',
baseURL: config.OCO_OLLAMA_API_URL!
});
case OCO_AI_PROVIDER_ENUM.ANTHROPIC: case OCO_AI_PROVIDER_ENUM.ANTHROPIC:
return new AnthropicEngine({ return new AnthropicEngine(DEFAULT_CONFIG);
...DEFAULT_CONFIG,
apiKey: config.OCO_ANTHROPIC_API_KEY!
});
case OCO_AI_PROVIDER_ENUM.TEST: case OCO_AI_PROVIDER_ENUM.TEST:
return new TestAi(config.OCO_TEST_MOCK_TYPE as TestMockType); return new TestAi(config.OCO_TEST_MOCK_TYPE as TestMockType);
case OCO_AI_PROVIDER_ENUM.GEMINI: case OCO_AI_PROVIDER_ENUM.GEMINI:
return new Gemini({ return new GeminiEngine(DEFAULT_CONFIG);
...DEFAULT_CONFIG,
apiKey: config.OCO_GEMINI_API_KEY!,
baseURL: config.OCO_GEMINI_BASE_PATH!
});
case OCO_AI_PROVIDER_ENUM.AZURE: case OCO_AI_PROVIDER_ENUM.AZURE:
return new AzureEngine({ return new AzureEngine(DEFAULT_CONFIG);
...DEFAULT_CONFIG,
apiKey: config.OCO_AZURE_API_KEY!
});
case OCO_AI_PROVIDER_ENUM.FLOWISE: case OCO_AI_PROVIDER_ENUM.FLOWISE:
return new FlowiseAi({ return new FlowiseEngine(DEFAULT_CONFIG);
...DEFAULT_CONFIG,
baseURL: config.OCO_FLOWISE_ENDPOINT || DEFAULT_CONFIG.baseURL,
apiKey: config.OCO_FLOWISE_API_KEY!
});
default: default:
return new OpenAiEngine({ return new OpenAiEngine(DEFAULT_CONFIG);
...DEFAULT_CONFIG,
apiKey: config.OCO_OPENAI_API_KEY!
});
} }
} }

View File

@@ -1,5 +1,6 @@
import { existsSync, readFileSync, rmSync } from 'fs'; import { existsSync, readFileSync, rmSync } from 'fs';
import { import {
CONFIG_KEYS,
DEFAULT_CONFIG, DEFAULT_CONFIG,
getConfig, getConfig,
setConfig setConfig
@@ -50,14 +51,13 @@ describe('config', () => {
describe('getConfig', () => { describe('getConfig', () => {
it('should prioritize local .env over global .opencommit config', async () => { it('should prioritize local .env over global .opencommit config', async () => {
globalConfigFile = await generateConfig('.opencommit', { globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'global-key', OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-3.5-turbo', OCO_MODEL: 'gpt-3.5-turbo',
OCO_LANGUAGE: 'en' OCO_LANGUAGE: 'en'
}); });
envConfigFile = await generateConfig('.env', { envConfigFile = await generateConfig('.env', {
OCO_OPENAI_API_KEY: 'local-key', OCO_API_KEY: 'local-key',
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key',
OCO_LANGUAGE: 'fr' OCO_LANGUAGE: 'fr'
}); });
@@ -67,22 +67,21 @@ describe('config', () => {
}); });
expect(config).not.toEqual(null); expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('local-key'); expect(config.OCO_API_KEY).toEqual('local-key');
expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo'); expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo');
expect(config.OCO_LANGUAGE).toEqual('fr'); expect(config.OCO_LANGUAGE).toEqual('fr');
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
}); });
it('should fallback to global config when local config is not set', async () => { it('should fallback to global config when local config is not set', async () => {
globalConfigFile = await generateConfig('.opencommit', { globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'global-key', OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4', OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'de', OCO_LANGUAGE: 'de',
OCO_DESCRIPTION: 'true' OCO_DESCRIPTION: 'true'
}); });
envConfigFile = await generateConfig('.env', { envConfigFile = await generateConfig('.env', {
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key' OCO_API_URL: 'local-api-url'
}); });
const config = getConfig({ const config = getConfig({
@@ -91,8 +90,8 @@ describe('config', () => {
}); });
expect(config).not.toEqual(null); expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key'); expect(config.OCO_API_KEY).toEqual('global-key');
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key'); expect(config.OCO_API_URL).toEqual('local-api-url');
expect(config.OCO_MODEL).toEqual('gpt-4'); expect(config.OCO_MODEL).toEqual('gpt-4');
expect(config.OCO_LANGUAGE).toEqual('de'); expect(config.OCO_LANGUAGE).toEqual('de');
expect(config.OCO_DESCRIPTION).toEqual(true); expect(config.OCO_DESCRIPTION).toEqual(true);
@@ -124,7 +123,7 @@ describe('config', () => {
it('should handle empty local config correctly', async () => { it('should handle empty local config correctly', async () => {
globalConfigFile = await generateConfig('.opencommit', { globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'global-key', OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4', OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'es' OCO_LANGUAGE: 'es'
}); });
@@ -137,20 +136,20 @@ describe('config', () => {
}); });
expect(config).not.toEqual(null); expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key'); expect(config.OCO_API_KEY).toEqual('global-key');
expect(config.OCO_MODEL).toEqual('gpt-4'); expect(config.OCO_MODEL).toEqual('gpt-4');
expect(config.OCO_LANGUAGE).toEqual('es'); expect(config.OCO_LANGUAGE).toEqual('es');
}); });
it('should override global config with null values in local .env', async () => { it('should override global config with null values in local .env', async () => {
globalConfigFile = await generateConfig('.opencommit', { globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'global-key', OCO_API_KEY: 'global-key',
OCO_MODEL: 'gpt-4', OCO_MODEL: 'gpt-4',
OCO_LANGUAGE: 'es' OCO_LANGUAGE: 'es'
}); });
envConfigFile = await generateConfig('.env', { envConfigFile = await generateConfig('.env', {
OCO_OPENAI_API_KEY: 'null' OCO_API_KEY: 'null'
}); });
const config = getConfig({ const config = getConfig({
@@ -159,7 +158,7 @@ describe('config', () => {
}); });
expect(config).not.toEqual(null); expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual(null); expect(config.OCO_API_KEY).toEqual(null);
}); });
it('should handle empty global config', async () => { it('should handle empty global config', async () => {
@@ -172,7 +171,7 @@ describe('config', () => {
}); });
expect(config).not.toEqual(null); expect(config).not.toEqual(null);
expect(config.OCO_OPENAI_API_KEY).toEqual(undefined); expect(config.OCO_API_KEY).toEqual(undefined);
}); });
}); });
@@ -188,12 +187,12 @@ describe('config', () => {
expect(isGlobalConfigFileExist).toBe(false); expect(isGlobalConfigFileExist).toBe(false);
await setConfig( await setConfig(
[['OCO_OPENAI_API_KEY', 'persisted-key_1']], [[CONFIG_KEYS.OCO_API_KEY, 'persisted-key_1']],
globalConfigFile.filePath globalConfigFile.filePath
); );
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8'); const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key_1'); expect(fileContent).toContain('OCO_API_KEY=persisted-key_1');
Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => { Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => {
expect(fileContent).toContain(`${key}=${value}`); expect(fileContent).toContain(`${key}=${value}`);
}); });
@@ -203,42 +202,48 @@ describe('config', () => {
globalConfigFile = await generateConfig('.opencommit', {}); globalConfigFile = await generateConfig('.opencommit', {});
await setConfig( await setConfig(
[ [
['OCO_OPENAI_API_KEY', 'new-key'], [CONFIG_KEYS.OCO_API_KEY, 'new-key'],
['OCO_MODEL', 'gpt-4'] [CONFIG_KEYS.OCO_MODEL, 'gpt-4']
], ],
globalConfigFile.filePath globalConfigFile.filePath
); );
const config = getConfig({ globalPath: globalConfigFile.filePath }); const config = getConfig({
expect(config.OCO_OPENAI_API_KEY).toEqual('new-key'); globalPath: globalConfigFile.filePath
});
expect(config.OCO_API_KEY).toEqual('new-key');
expect(config.OCO_MODEL).toEqual('gpt-4'); expect(config.OCO_MODEL).toEqual('gpt-4');
}); });
it('should update existing config values', async () => { it('should update existing config values', async () => {
globalConfigFile = await generateConfig('.opencommit', { globalConfigFile = await generateConfig('.opencommit', {
OCO_OPENAI_API_KEY: 'initial-key' OCO_API_KEY: 'initial-key'
}); });
await setConfig( await setConfig(
[['OCO_OPENAI_API_KEY', 'updated-key']], [[CONFIG_KEYS.OCO_API_KEY, 'updated-key']],
globalConfigFile.filePath globalConfigFile.filePath
); );
const config = getConfig({ globalPath: globalConfigFile.filePath }); const config = getConfig({
expect(config.OCO_OPENAI_API_KEY).toEqual('updated-key'); globalPath: globalConfigFile.filePath
});
expect(config.OCO_API_KEY).toEqual('updated-key');
}); });
it('should handle boolean and numeric values correctly', async () => { it('should handle boolean and numeric values correctly', async () => {
globalConfigFile = await generateConfig('.opencommit', {}); globalConfigFile = await generateConfig('.opencommit', {});
await setConfig( await setConfig(
[ [
['OCO_TOKENS_MAX_INPUT', '8192'], [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT, '8192'],
['OCO_DESCRIPTION', 'true'], [CONFIG_KEYS.OCO_DESCRIPTION, 'true'],
['OCO_ONE_LINE_COMMIT', 'false'] [CONFIG_KEYS.OCO_ONE_LINE_COMMIT, 'false']
], ],
globalConfigFile.filePath globalConfigFile.filePath
); );
const config = getConfig({ globalPath: globalConfigFile.filePath }); const config = getConfig({
globalPath: globalConfigFile.filePath
});
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192); expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192);
expect(config.OCO_DESCRIPTION).toEqual(true); expect(config.OCO_DESCRIPTION).toEqual(true);
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false); expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
@@ -266,12 +271,12 @@ describe('config', () => {
expect(isGlobalConfigFileExist).toBe(false); expect(isGlobalConfigFileExist).toBe(false);
await setConfig( await setConfig(
[['OCO_OPENAI_API_KEY', 'persisted-key']], [[CONFIG_KEYS.OCO_API_KEY, 'persisted-key']],
globalConfigFile.filePath globalConfigFile.filePath
); );
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8'); const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key'); expect(fileContent).toContain('OCO_API_KEY=persisted-key');
}); });
it('should set multiple configs in a row and keep the changes', async () => { it('should set multiple configs in a row and keep the changes', async () => {
@@ -279,14 +284,17 @@ describe('config', () => {
expect(isGlobalConfigFileExist).toBe(false); expect(isGlobalConfigFileExist).toBe(false);
await setConfig( await setConfig(
[['OCO_OPENAI_API_KEY', 'persisted-key']], [[CONFIG_KEYS.OCO_API_KEY, 'persisted-key']],
globalConfigFile.filePath globalConfigFile.filePath
); );
const fileContent1 = readFileSync(globalConfigFile.filePath, 'utf8'); const fileContent1 = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent1).toContain('OCO_OPENAI_API_KEY=persisted-key'); expect(fileContent1).toContain('OCO_API_KEY=persisted-key');
await setConfig([['OCO_MODEL', 'gpt-4']], globalConfigFile.filePath); await setConfig(
[[CONFIG_KEYS.OCO_MODEL, 'gpt-4']],
globalConfigFile.filePath
);
const fileContent2 = readFileSync(globalConfigFile.filePath, 'utf8'); const fileContent2 = readFileSync(globalConfigFile.filePath, 'utf8');
expect(fileContent2).toContain('OCO_MODEL=gpt-4'); expect(fileContent2).toContain('OCO_MODEL=gpt-4');

View File

@@ -1,4 +1,4 @@
import { Gemini } from '../../src/engine/gemini'; import { GeminiEngine } from '../../src/engine/gemini';
import { GenerativeModel, GoogleGenerativeAI } from '@google/generative-ai'; import { GenerativeModel, GoogleGenerativeAI } from '@google/generative-ai';
import { import {
@@ -9,7 +9,7 @@ import {
import { OpenAI } from 'openai'; import { OpenAI } from 'openai';
describe('Gemini', () => { describe('Gemini', () => {
let gemini: Gemini; let gemini: GeminiEngine;
let mockConfig: ConfigType; let mockConfig: ConfigType;
let mockGoogleGenerativeAi: GoogleGenerativeAI; let mockGoogleGenerativeAi: GoogleGenerativeAI;
let mockGenerativeModel: GenerativeModel; let mockGenerativeModel: GenerativeModel;
@@ -20,8 +20,8 @@ describe('Gemini', () => {
const mockGemini = () => { const mockGemini = () => {
mockConfig = getConfig() as ConfigType; mockConfig = getConfig() as ConfigType;
gemini = new Gemini({ gemini = new GeminiEngine({
apiKey: mockConfig.OCO_GEMINI_API_KEY, apiKey: mockConfig.OCO_API_KEY,
model: mockConfig.OCO_MODEL model: mockConfig.OCO_MODEL
}); });
}; };
@@ -45,12 +45,10 @@ describe('Gemini', () => {
mockConfig = getConfig() as ConfigType; mockConfig = getConfig() as ConfigType;
mockConfig.OCO_AI_PROVIDER = OCO_AI_PROVIDER_ENUM.GEMINI; mockConfig.OCO_AI_PROVIDER = OCO_AI_PROVIDER_ENUM.GEMINI;
mockConfig.OCO_GEMINI_API_KEY = 'mock-api-key'; mockConfig.OCO_API_KEY = 'mock-api-key';
mockConfig.OCO_MODEL = 'gemini-1.5-flash'; mockConfig.OCO_MODEL = 'gemini-1.5-flash';
mockGoogleGenerativeAi = new GoogleGenerativeAI( mockGoogleGenerativeAi = new GoogleGenerativeAI(mockConfig.OCO_API_KEY);
mockConfig.OCO_GEMINI_API_KEY
);
mockGenerativeModel = mockGoogleGenerativeAi.getGenerativeModel({ mockGenerativeModel = mockGoogleGenerativeAi.getGenerativeModel({
model: mockConfig.OCO_MODEL model: mockConfig.OCO_MODEL
}); });