mirror of
https://github.com/di-sukharev/opencommit.git
synced 2026-01-13 07:38:01 -05:00
Compare commits
52 Commits
v1.1.51
...
github-act
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61b145455d | ||
|
|
0b8dc12047 | ||
|
|
1a1134a010 | ||
|
|
d40e2ca4af | ||
|
|
540b4b3bf1 | ||
|
|
0429b92120 | ||
|
|
c6036f0570 | ||
|
|
e2eb13a678 | ||
|
|
c23b0b4806 | ||
|
|
ee540108cd | ||
|
|
a123fbd703 | ||
|
|
aff1d902d2 | ||
|
|
0d8469ee42 | ||
|
|
517734f293 | ||
|
|
6aff5ebef4 | ||
|
|
6afa493726 | ||
|
|
3eb319a919 | ||
|
|
54006826f8 | ||
|
|
f674e2d99a | ||
|
|
8140322c32 | ||
|
|
1cb8d580bb | ||
|
|
8daa3ca130 | ||
|
|
13015a9033 | ||
|
|
7b90b6a287 | ||
|
|
598881a41c | ||
|
|
1080544631 | ||
|
|
de68e6cc7a | ||
|
|
5addb7df25 | ||
|
|
e447575980 | ||
|
|
ffebbc6e1b | ||
|
|
dab0f58d14 | ||
|
|
226e21c28f | ||
|
|
0bb89abccc | ||
|
|
ad70a90b1f | ||
|
|
4deaf56e5a | ||
|
|
7c1fc10248 | ||
|
|
0c25a9e32c | ||
|
|
3f5df6ef7c | ||
|
|
6cb85e40e9 | ||
|
|
ba82d4d476 | ||
|
|
9bf2ed34a5 | ||
|
|
f6ab25ed1b | ||
|
|
83abd5ffd6 | ||
|
|
42c26cbaaa | ||
|
|
51613c2aea | ||
|
|
f04757f8af | ||
|
|
70f048672c | ||
|
|
a8a548ba5a | ||
|
|
eb09d5f4f6 | ||
|
|
801f6a9e7a | ||
|
|
b4f1bbdfe0 | ||
|
|
1ecad09e44 |
@@ -21,8 +21,8 @@
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"no-console": "error",
|
||||
"sort-imports": "off",
|
||||
"import/order": "off",
|
||||
"sort-imports": "off",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"import/first": "error",
|
||||
|
||||
BIN
.github/opencommit-example.png
vendored
BIN
.github/opencommit-example.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 318 KiB After Width: | Height: | Size: 304 KiB |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,9 +1,7 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
out/
|
||||
temp/
|
||||
build/
|
||||
dist/
|
||||
application.log
|
||||
.DS_Store
|
||||
/*.env
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
/build
|
||||
/dist
|
||||
/dist
|
||||
/out
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Dima Sukharev
|
||||
Copyright (c) Dima Sukharev, https://github.com/di-sukharev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
129
README.md
129
README.md
@@ -5,7 +5,7 @@
|
||||
<h4 align="center">Follow the bird <a href="https://twitter.com/io_Y_oi"><img src="https://img.shields.io/twitter/follow/io_Y_oi?style=flat&label=io_Y_oi&logo=twitter&color=0bf&logoColor=fff" align="center"></a>
|
||||
</h4>
|
||||
</div>
|
||||
<h2>GPT CLI to auto-generate impressive commits in 1 second</h2>
|
||||
<h2>Auto-generate meaningful commits in 1 second</h2>
|
||||
<p>Killing lame commits with AI 🤯🔫</p>
|
||||
<a href="https://www.npmjs.com/package/opencommit"><img src="https://img.shields.io/npm/v/opencommit" alt="Current version"></a>
|
||||
</div>
|
||||
@@ -18,7 +18,64 @@
|
||||
|
||||
All the commits in this repo are done with OpenCommit — look into [the commits](https://github.com/di-sukharev/opencommit/commit/eae7618d575ee8d2e9fff5de56da79d40c4bc5fc) to see how OpenCommit works. Emoji and long commit description text is configurable.
|
||||
|
||||
## Setup
|
||||
## Setup OpenCommit as a Github Action 🔥
|
||||
|
||||
OpenCommit is now available as a GitHub Action which automatically improves all new commits messages when you push to remote!
|
||||
|
||||
This is great if you want to make sure all of the commits in all of repository branches are meaningful and not lame like `fix1` or `done2`.
|
||||
|
||||
Create a file `.github/workflows/opencommit.yml` with contents below:
|
||||
|
||||
```yml
|
||||
name: 'OpenCommit Action'
|
||||
|
||||
on:
|
||||
push:
|
||||
# this list of branches is often enough,
|
||||
# but you may still ignore other public branches
|
||||
branches-ignore: [main master dev development release]
|
||||
|
||||
jobs:
|
||||
opencommit:
|
||||
name: OpenCommit
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Setup Node.js Environment
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: di-sukharev/opencommit@github-action-v1.0.1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
env:
|
||||
# set openAI api key in repo actions secrets,
|
||||
# for openAI keys go to: https://platform.openai.com/account/api-keys
|
||||
# for repo secret go to: <your_repo_url>/settings/secrets/actions
|
||||
OCO_OPENAI_API_KEY: ${{ secrets.OCO_OPENAI_API_KEY }}
|
||||
|
||||
# customization
|
||||
OCO_OPENAI_MAX_TOKENS: 500
|
||||
OCO_OPENAI_BASE_PATH: ''
|
||||
OCO_DESCRIPTION: false
|
||||
OCO_EMOJI: false
|
||||
OCO_MODEL: gpt-3.5-turbo
|
||||
OCO_LANGUAGE: en
|
||||
```
|
||||
|
||||
That is it. Now when you push to any branch in your repo — all NEW commits are being improved by never-tired-AI.
|
||||
|
||||
Make sure you exclude public collaboration branches (`main`, `dev`, `etc`) in `branches-ignore`, so OpenCommit does not rebase commits there when improving the messages.
|
||||
|
||||
Interactive rebase (`rebase -i`) changes commit SHA, so commit history in remote becomes different with your local branch history. It's ok when you work on the branch alone, but may be inconvenient for other collaborators.
|
||||
|
||||
## Setup OpenCommit as a CLI
|
||||
|
||||
You can use OpenCommit by simply running it via CLI like this `oc`. 2 seconds and your staged changes are committed with a meaningful message.
|
||||
|
||||
1. Install OpenCommit globally to use in any repository:
|
||||
|
||||
@@ -31,7 +88,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
|
||||
3. Set the key to OpenCommit config:
|
||||
|
||||
```sh
|
||||
opencommit config set OPENAI_API_KEY=<your_api_key>
|
||||
opencommit config set OCO_OPENAI_API_KEY=<your_api_key>
|
||||
```
|
||||
|
||||
Your api key is stored locally in `~/.opencommit` config file.
|
||||
@@ -52,52 +109,70 @@ git add <files...>
|
||||
oc
|
||||
```
|
||||
|
||||
## Features
|
||||
## Configuration
|
||||
|
||||
### Preface commits with emoji 🤠
|
||||
### Local per repo configuration
|
||||
|
||||
[GitMoji](https://gitmoji.dev/) convention is used.
|
||||
Create an `.env` file and add OpenCommit config variables there like this:
|
||||
|
||||
To add emoji:
|
||||
|
||||
```sh
|
||||
oc config set emoji=true
|
||||
```env
|
||||
OCO_OPENAI_API_KEY=<your openAI API token>
|
||||
OCO_OPENAI_MAX_TOKENS=<max response tokens from openAI API>
|
||||
OCO_OPENAI_BASE_PATH=<may be used to set proxy path to openAI api>
|
||||
OCO_DESCRIPTION=<postface a message with ~3 sentences description>
|
||||
OCO_EMOJI=<add GitMoji>
|
||||
OCO_MODEL=<either gpt-3.5-turbo or gpt-4>
|
||||
OCO_LANGUAGE=<locale, scroll to the bottom to see options>
|
||||
```
|
||||
|
||||
To remove emoji:
|
||||
### Global config for all repos
|
||||
|
||||
Local config still has more priority as Global config, but you may set `OCO_MODEL` and `OCO_LOCALE` globally and set local configs for `OCO_EMOJI` and `OCO_DESCRIPTION` per repo which is more convenient.
|
||||
|
||||
Simply run any of the variable above like this:
|
||||
|
||||
```sh
|
||||
oc config set emoji=false
|
||||
oc config set OCO_OPENAI_API_KEY=gpt-4
|
||||
```
|
||||
|
||||
### Postface commits with descriptions of changes
|
||||
|
||||
To add descriptions:
|
||||
Configure [GitMoji](https://gitmoji.dev/) to preface a message.
|
||||
|
||||
```sh
|
||||
oc config set description=true
|
||||
oc config set OCO_EMOJI=true
|
||||
```
|
||||
|
||||
To remove description:
|
||||
To remove preface emoji:
|
||||
|
||||
```sh
|
||||
oc config set description=false
|
||||
oc config set OCO_EMOJI=false
|
||||
```
|
||||
|
||||
### Internationalization support
|
||||
### Switch to GPT-4
|
||||
|
||||
To specify the language used to generate commit messages:
|
||||
By default OpenCommit uses GPT-3.5-turbo (ChatGPT).
|
||||
|
||||
You may switch to GPT-4 which performs better, but costs ~x15 times more 🤠
|
||||
|
||||
```sh
|
||||
oc config set OCO_MODEL=gpt-4
|
||||
```
|
||||
|
||||
Make sure you do lowercase `gpt-4` and you have API access to the 4th model. Even if you have ChatGPT+ it doesn't necessarily mean that you have API access to GPT-4.
|
||||
|
||||
## Locale configuration
|
||||
|
||||
To globally specify the language used to generate commit messages:
|
||||
|
||||
```sh
|
||||
# de, German ,Deutsch
|
||||
oc config set language=de
|
||||
oc config set language=German
|
||||
oc config set language=Deutsch
|
||||
oc config set OCO_LANGUAGE=de
|
||||
oc config set OCO_LANGUAGE=German
|
||||
oc config set OCO_LANGUAGE=Deutsch
|
||||
|
||||
# fr, French, française
|
||||
oc config set language=fr
|
||||
oc config set language=French
|
||||
oc config set language=française
|
||||
oc config set OCO_LANGUAGE=fr
|
||||
oc config set OCO_LANGUAGE=French
|
||||
oc config set OCO_LANGUAGE=française
|
||||
```
|
||||
|
||||
The default language set is **English**
|
||||
@@ -130,7 +205,7 @@ This is useful for preventing opencommit from uploading artifacts and large file
|
||||
|
||||
By default, opencommit ignores files matching: `*-lock.*` and `*.lock`
|
||||
|
||||
## Git hook
|
||||
## Git hook (KILLER FEATURE)
|
||||
|
||||
You can set OpenCommit as Git [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. Hook integrates with you IDE Source Control and allows you edit the message before commit.
|
||||
|
||||
|
||||
29
action.yml
Normal file
29
action.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: 'OpenCommit — improve commits with AI 🧙'
|
||||
description: 'Replaces lame commit messages with meaningful AI-generated messages when you push to remote'
|
||||
author: 'https://github.com/di-sukharev'
|
||||
repo: 'https://github.com/di-sukharev/opencommit/tree/github-action'
|
||||
branding:
|
||||
icon: 'git-commit'
|
||||
color: 'green'
|
||||
keywords:
|
||||
[
|
||||
'git',
|
||||
'chatgpt',
|
||||
'gpt',
|
||||
'ai',
|
||||
'openai',
|
||||
'opencommit',
|
||||
'aicommit',
|
||||
'aicommits',
|
||||
'gptcommit',
|
||||
'commit'
|
||||
]
|
||||
|
||||
inputs:
|
||||
GITHUB_TOKEN:
|
||||
description: 'GitHub token'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'out/github-action.cjs'
|
||||
@@ -1,14 +1,24 @@
|
||||
import { build } from 'esbuild'
|
||||
import fs from 'fs'
|
||||
import { build } from 'esbuild';
|
||||
import fs from 'fs';
|
||||
|
||||
await build({
|
||||
entryPoints: ['./src/cli.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: './out/cli.cjs',
|
||||
entryPoints: ['./src/cli.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: './out/cli.cjs'
|
||||
});
|
||||
|
||||
const wasmFile = fs.readFileSync('./node_modules/@dqbd/tiktoken/lite/tiktoken_bg.wasm')
|
||||
await build({
|
||||
entryPoints: ['./src/github-action.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: './out/github-action.cjs'
|
||||
});
|
||||
|
||||
fs.writeFileSync('./out/tiktoken_bg.wasm', wasmFile)
|
||||
const wasmFile = fs.readFileSync(
|
||||
'./node_modules/@dqbd/tiktoken/lite/tiktoken_bg.wasm'
|
||||
);
|
||||
|
||||
fs.writeFileSync('./out/tiktoken_bg.wasm', wasmFile);
|
||||
|
||||
22124
out/cli.cjs
Executable file
22124
out/cli.cjs
Executable file
File diff suppressed because one or more lines are too long
27337
out/github-action.cjs
Normal file
27337
out/github-action.cjs
Normal file
File diff suppressed because one or more lines are too long
BIN
out/tiktoken_bg.wasm
Normal file
BIN
out/tiktoken_bg.wasm
Normal file
Binary file not shown.
428
package-lock.json
generated
428
package-lock.json
generated
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"name": "opencommit",
|
||||
"version": "1.1.51",
|
||||
"version": "2.0.17",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "opencommit",
|
||||
"version": "1.1.51",
|
||||
"version": "2.0.17",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^5.1.1",
|
||||
"@clack/prompts": "^0.6.1",
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
"@octokit/webhooks-schemas": "^6.11.0",
|
||||
"@octokit/webhooks-types": "^6.11.0",
|
||||
"axios": "^1.3.4",
|
||||
"chalk": "^5.2.0",
|
||||
"cleye": "^1.3.2",
|
||||
@@ -21,6 +27,7 @@
|
||||
},
|
||||
"bin": {
|
||||
"oc": "out/cli.cjs",
|
||||
"oco": "out/cli.cjs",
|
||||
"opencommit": "out/cli.cjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -37,6 +44,112 @@
|
||||
"typescript": "^4.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/exec": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
|
||||
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
|
||||
"dependencies": {
|
||||
"@actions/io": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
|
||||
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@octokit/core": "^3.6.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@octokit/auth-token": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
|
||||
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@octokit/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.6.3",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@octokit/endpoint": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
|
||||
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@octokit/graphql": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
|
||||
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^5.6.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.1.0",
|
||||
"@octokit/types": "^6.16.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github/node_modules/@octokit/request-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
|
||||
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
|
||||
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/io": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
|
||||
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
|
||||
},
|
||||
"node_modules/@clack/core": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@clack/core/-/core-0.3.2.tgz",
|
||||
@@ -83,6 +196,11 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@dqbd/tiktoken": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@dqbd/tiktoken/-/tiktoken-1.0.6.tgz",
|
||||
"integrity": "sha512-umSdeZTy/SbPPKVuZKV/XKyFPmXSN145CcM3iHjBbmhlohBJg7vaDp4cPCW+xNlWL6L2U1sp7T2BD+di2sUKdA=="
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.15.18",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
|
||||
@@ -240,6 +358,231 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz",
|
||||
"integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.2.tgz",
|
||||
"integrity": "sha512-OaS7Ol4Y+U50PbejfzQflGWRMxO04nYWO5ZBv6JerqMKE2WS/tI9VoVDDPXHBlRMGG2fOdKwtVGlFfc7AVIstw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/auth-token/node_modules/@octokit/types": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.2.tgz",
|
||||
"integrity": "sha512-9BjDxjgQIvCjNWZsbqyH5QC2Yni16oaE6xL+8SUBMzcYPF4TGQBXGA97Cl3KceK9mwiNMb1mOYCz6FbCCLEL+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz",
|
||||
"integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^3.0.0",
|
||||
"@octokit/graphql": "^5.0.0",
|
||||
"@octokit/request": "^6.0.0",
|
||||
"@octokit/request-error": "^3.0.0",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.2.tgz",
|
||||
"integrity": "sha512-OaS7Ol4Y+U50PbejfzQflGWRMxO04nYWO5ZBv6JerqMKE2WS/tI9VoVDDPXHBlRMGG2fOdKwtVGlFfc7AVIstw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/types": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.2.tgz",
|
||||
"integrity": "sha512-9BjDxjgQIvCjNWZsbqyH5QC2Yni16oaE6xL+8SUBMzcYPF4TGQBXGA97Cl3KceK9mwiNMb1mOYCz6FbCCLEL+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.5.tgz",
|
||||
"integrity": "sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^9.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.2.tgz",
|
||||
"integrity": "sha512-OaS7Ol4Y+U50PbejfzQflGWRMxO04nYWO5ZBv6JerqMKE2WS/tI9VoVDDPXHBlRMGG2fOdKwtVGlFfc7AVIstw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/types": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.2.tgz",
|
||||
"integrity": "sha512-9BjDxjgQIvCjNWZsbqyH5QC2Yni16oaE6xL+8SUBMzcYPF4TGQBXGA97Cl3KceK9mwiNMb1mOYCz6FbCCLEL+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz",
|
||||
"integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/request": "^6.0.0",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.2.tgz",
|
||||
"integrity": "sha512-OaS7Ol4Y+U50PbejfzQflGWRMxO04nYWO5ZBv6JerqMKE2WS/tI9VoVDDPXHBlRMGG2fOdKwtVGlFfc7AVIstw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/types": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.2.tgz",
|
||||
"integrity": "sha512-9BjDxjgQIvCjNWZsbqyH5QC2Yni16oaE6xL+8SUBMzcYPF4TGQBXGA97Cl3KceK9mwiNMb1mOYCz6FbCCLEL+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "12.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
|
||||
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "2.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
|
||||
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
|
||||
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.39.0",
|
||||
"deprecation": "^2.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz",
|
||||
"integrity": "sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^7.0.0",
|
||||
"@octokit/request-error": "^3.0.0",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
|
||||
"integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^9.0.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.2.tgz",
|
||||
"integrity": "sha512-OaS7Ol4Y+U50PbejfzQflGWRMxO04nYWO5ZBv6JerqMKE2WS/tI9VoVDDPXHBlRMGG2fOdKwtVGlFfc7AVIstw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/request-error/node_modules/@octokit/types": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.2.tgz",
|
||||
"integrity": "sha512-9BjDxjgQIvCjNWZsbqyH5QC2Yni16oaE6xL+8SUBMzcYPF4TGQBXGA97Cl3KceK9mwiNMb1mOYCz6FbCCLEL+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.2.tgz",
|
||||
"integrity": "sha512-OaS7Ol4Y+U50PbejfzQflGWRMxO04nYWO5ZBv6JerqMKE2WS/tI9VoVDDPXHBlRMGG2fOdKwtVGlFfc7AVIstw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/types": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.2.tgz",
|
||||
"integrity": "sha512-9BjDxjgQIvCjNWZsbqyH5QC2Yni16oaE6xL+8SUBMzcYPF4TGQBXGA97Cl3KceK9mwiNMb1mOYCz6FbCCLEL+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "6.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
|
||||
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/webhooks-schemas": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/webhooks-schemas/-/webhooks-schemas-6.11.0.tgz",
|
||||
"integrity": "sha512-ekca2jZhb2vfQy43rjvJoV77IwEKvA42BmJ2m8H3WaNfG9BF05RodnFjh3MSOksNseoNO8w8IPLZ3d5546NH2w=="
|
||||
},
|
||||
"node_modules/@octokit/webhooks-types": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.11.0.tgz",
|
||||
"integrity": "sha512-AanzbulOHljrku1NGfafxdpTCfw2ENaWzH01N2vqQM+cUFbk868Cgh0xylz0JIM9BoKbfI++bdD6EYX0Q/UTEw=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
@@ -647,6 +990,11 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
|
||||
@@ -876,6 +1224,11 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
@@ -2033,6 +2386,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
|
||||
@@ -2258,6 +2619,25 @@
|
||||
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
|
||||
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
|
||||
@@ -2287,7 +2667,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -2897,6 +3276,11 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
@@ -2966,6 +3350,14 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -3011,6 +3403,11 @@
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
@@ -3025,6 +3422,14 @@
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
@@ -3039,6 +3444,20 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -3106,8 +3525,7 @@
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "opencommit",
|
||||
"version": "1.1.51",
|
||||
"description": "GPT CLI to auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
|
||||
"version": "2.0.17",
|
||||
"description": "Auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
|
||||
"keywords": [
|
||||
"git",
|
||||
"chatgpt",
|
||||
@@ -43,6 +43,7 @@
|
||||
"dev": "ts-node ./src/cli.ts",
|
||||
"build": "rimraf out && node esbuild.config.js",
|
||||
"deploy": "npm run build && npm version patch && npm publish --tag latest",
|
||||
"build:push": "npm run build && git add . && git commit -m 'build' && git push",
|
||||
"lint": "eslint src --ext ts && tsc --noEmit",
|
||||
"format": "prettier --write src"
|
||||
},
|
||||
@@ -60,8 +61,13 @@
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^5.1.1",
|
||||
"@clack/prompts": "^0.6.1",
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
"@octokit/webhooks-schemas": "^6.11.0",
|
||||
"@octokit/webhooks-types": "^6.11.0",
|
||||
"axios": "^1.3.4",
|
||||
"chalk": "^5.2.0",
|
||||
"cleye": "^1.3.2",
|
||||
|
||||
33
src/api.ts
33
src/api.ts
@@ -11,8 +11,9 @@ import { CONFIG_MODES, getConfig } from './commands/config';
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
let apiKey = config?.OPENAI_API_KEY;
|
||||
let basePath = config?.OPENAI_BASE_PATH;
|
||||
let maxTokens = config?.OCO_OPENAI_MAX_TOKENS;
|
||||
let basePath = config?.OCO_OPENAI_BASE_PATH;
|
||||
let apiKey = config?.OCO_OPENAI_API_KEY;
|
||||
|
||||
const [command, mode] = process.argv.slice(2);
|
||||
|
||||
@@ -20,7 +21,7 @@ if (!apiKey && command !== 'config' && mode !== CONFIG_MODES.set) {
|
||||
intro('opencommit');
|
||||
|
||||
outro(
|
||||
'OPENAI_API_KEY is not set, please run `oc config set OPENAI_API_KEY=<your token>. Make sure you add payment details, so API works.`'
|
||||
'OCO_OPENAI_API_KEY is not set, please run `oc config set OCO_OPENAI_API_KEY=<your token>. Make sure you add payment details, so API works.`'
|
||||
);
|
||||
outro(
|
||||
'For help look into README https://github.com/di-sukharev/opencommit#setup'
|
||||
@@ -29,6 +30,8 @@ if (!apiKey && command !== 'config' && mode !== CONFIG_MODES.set) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const MODEL = config?.OCO_MODEL || 'gpt-3.5-turbo';
|
||||
|
||||
class OpenAi {
|
||||
private openAiApiConfiguration = new OpenAiApiConfiguration({
|
||||
apiKey: apiKey
|
||||
@@ -45,20 +48,24 @@ class OpenAi {
|
||||
public generateCommitMessage = async (
|
||||
messages: Array<ChatCompletionRequestMessage>
|
||||
): Promise<string | undefined> => {
|
||||
const params = {
|
||||
model: MODEL,
|
||||
messages,
|
||||
temperature: 0,
|
||||
top_p: 0.1,
|
||||
max_tokens: maxTokens || 500
|
||||
};
|
||||
try {
|
||||
const { data } = await this.openAI.createChatCompletion({
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages,
|
||||
temperature: 0,
|
||||
top_p: 0.1,
|
||||
max_tokens: 196
|
||||
});
|
||||
const { data } = await this.openAI.createChatCompletion(params);
|
||||
|
||||
const message = data.choices[0].message;
|
||||
|
||||
return message?.content;
|
||||
} catch (error: unknown) {
|
||||
outro(`${chalk.red('✖')} ${error}`);
|
||||
} catch (error) {
|
||||
outro(`${chalk.red('✖')} ${JSON.stringify(params)}`);
|
||||
|
||||
const err = error as Error;
|
||||
outro(`${chalk.red('✖')} ${err?.message || err}`);
|
||||
|
||||
if (
|
||||
axios.isAxiosError<{ error?: { message: string } }>(error) &&
|
||||
@@ -72,7 +79,7 @@ class OpenAi {
|
||||
);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { execa } from 'execa';
|
||||
import {
|
||||
GenerateCommitMessageErrorEnum,
|
||||
generateCommitMessageWithChatCompletion
|
||||
generateCommitMessageByDiff
|
||||
} from '../generateCommitMessageFromGitDiff';
|
||||
import {
|
||||
assertGitRepo,
|
||||
@@ -35,103 +35,98 @@ const generateCommitMessageFromGitDiff = async (
|
||||
|
||||
const commitSpinner = spinner();
|
||||
commitSpinner.start('Generating the commit message');
|
||||
const commitMessage = await generateCommitMessageWithChatCompletion(diff);
|
||||
try {
|
||||
const commitMessage = await generateCommitMessageByDiff(diff);
|
||||
|
||||
// TODO: show proper error messages
|
||||
if (typeof commitMessage !== 'string') {
|
||||
const errorMessages = {
|
||||
[GenerateCommitMessageErrorEnum.emptyMessage]:
|
||||
'empty openAI response, weird, try again',
|
||||
[GenerateCommitMessageErrorEnum.internalError]:
|
||||
'internal error, try again',
|
||||
[GenerateCommitMessageErrorEnum.tooMuchTokens]:
|
||||
'too much tokens in git diff, stage and commit files in parts'
|
||||
};
|
||||
commitSpinner.stop('📝 Commit message generated');
|
||||
|
||||
outro(`${chalk.red('✖')} ${errorMessages[commitMessage.error]}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
commitSpinner.stop('📝 Commit message generated');
|
||||
|
||||
outro(
|
||||
`Commit message:
|
||||
outro(
|
||||
`Commit message:
|
||||
${chalk.grey('——————————————————')}
|
||||
${commitMessage}
|
||||
${chalk.grey('——————————————————')}`
|
||||
);
|
||||
);
|
||||
|
||||
const isCommitConfirmedByUser = await confirm({
|
||||
message: 'Confirm the commit message?'
|
||||
});
|
||||
const isCommitConfirmedByUser = await confirm({
|
||||
message: 'Confirm the commit message?'
|
||||
});
|
||||
|
||||
if (isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) {
|
||||
const { stdout } = await execa('git', [
|
||||
'commit',
|
||||
'-m',
|
||||
commitMessage,
|
||||
...extraArgs
|
||||
]);
|
||||
if (isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) {
|
||||
const { stdout } = await execa('git', [
|
||||
'commit',
|
||||
'-m',
|
||||
commitMessage,
|
||||
...extraArgs
|
||||
]);
|
||||
|
||||
outro(`${chalk.green('✔')} successfully committed`);
|
||||
outro(`${chalk.green('✔')} Successfully committed`);
|
||||
|
||||
outro(stdout);
|
||||
outro(stdout);
|
||||
|
||||
const remotes = await getGitRemotes();
|
||||
|
||||
if (!remotes.length) {
|
||||
const { stdout } = await execa('git', ['push']);
|
||||
if (stdout) outro(stdout);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (remotes.length === 1) {
|
||||
const isPushConfirmedByUser = await confirm({
|
||||
message: 'Do you want to run `git push`?'
|
||||
});
|
||||
|
||||
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) {
|
||||
const pushSpinner = spinner();
|
||||
|
||||
pushSpinner.start(`Running \`git push ${remotes[0]}\``);
|
||||
|
||||
const { stdout } = await execa('git', [
|
||||
'push',
|
||||
'--verbose',
|
||||
remotes[0]
|
||||
]);
|
||||
|
||||
pushSpinner.stop(
|
||||
`${chalk.green('✔')} successfully pushed all commits to ${remotes[0]}`
|
||||
);
|
||||
const remotes = await getGitRemotes();
|
||||
|
||||
if (!remotes.length) {
|
||||
const { stdout } = await execa('git', ['push']);
|
||||
if (stdout) outro(stdout);
|
||||
} else {
|
||||
outro('`git push` aborted');
|
||||
process.exit(0);
|
||||
}
|
||||
} else {
|
||||
const selectedRemote = (await select({
|
||||
message: 'Choose a remote to push to',
|
||||
options: remotes.map((remote) => ({ value: remote, label: remote }))
|
||||
})) as string;
|
||||
|
||||
if (!isCancel(selectedRemote)) {
|
||||
const pushSpinner = spinner();
|
||||
if (remotes.length === 1) {
|
||||
const isPushConfirmedByUser = await confirm({
|
||||
message: 'Do you want to run `git push`?'
|
||||
});
|
||||
|
||||
pushSpinner.start(`Running \`git push ${selectedRemote}\``);
|
||||
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) {
|
||||
const pushSpinner = spinner();
|
||||
|
||||
const { stdout } = await execa('git', ['push', selectedRemote]);
|
||||
pushSpinner.start(`Running \`git push ${remotes[0]}\``);
|
||||
|
||||
pushSpinner.stop(
|
||||
`${chalk.green(
|
||||
'✔'
|
||||
)} successfully pushed all commits to ${selectedRemote}`
|
||||
);
|
||||
const { stdout } = await execa('git', [
|
||||
'push',
|
||||
'--verbose',
|
||||
remotes[0]
|
||||
]);
|
||||
|
||||
if (stdout) outro(stdout);
|
||||
} else outro(`${chalk.gray('✖')} process cancelled`);
|
||||
pushSpinner.stop(
|
||||
`${chalk.green('✔')} Successfully pushed all commits to ${
|
||||
remotes[0]
|
||||
}`
|
||||
);
|
||||
|
||||
if (stdout) outro(stdout);
|
||||
} else {
|
||||
outro('`git push` aborted');
|
||||
process.exit(0);
|
||||
}
|
||||
} else {
|
||||
const selectedRemote = (await select({
|
||||
message: 'Choose a remote to push to',
|
||||
options: remotes.map((remote) => ({ value: remote, label: remote }))
|
||||
})) as string;
|
||||
|
||||
if (!isCancel(selectedRemote)) {
|
||||
const pushSpinner = spinner();
|
||||
|
||||
pushSpinner.start(`Running \`git push ${selectedRemote}\``);
|
||||
|
||||
const { stdout } = await execa('git', ['push', selectedRemote]);
|
||||
|
||||
pushSpinner.stop(
|
||||
`${chalk.green(
|
||||
'✔'
|
||||
)} Successfully pushed all commits to ${selectedRemote}`
|
||||
);
|
||||
|
||||
if (stdout) outro(stdout);
|
||||
} else outro(`${chalk.gray('✖')} process cancelled`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
commitSpinner.stop('📝 Commit message generated');
|
||||
|
||||
const err = error as Error;
|
||||
outro(`${chalk.red('✖')} ${err?.message || err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,12 +8,18 @@ import chalk from 'chalk';
|
||||
import { COMMANDS } from '../CommandsEnum';
|
||||
import { getI18nLocal } from '../i18n';
|
||||
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export enum CONFIG_KEYS {
|
||||
OPENAI_API_KEY = 'OPENAI_API_KEY',
|
||||
OPENAI_BASE_PATH = 'OPENAI_BASE_PATH',
|
||||
description = 'description',
|
||||
emoji = 'emoji',
|
||||
language = 'language'
|
||||
OCO_OPENAI_API_KEY = 'OCO_OPENAI_API_KEY',
|
||||
OCO_OPENAI_MAX_TOKENS = 'OCO_OPENAI_MAX_TOKENS',
|
||||
OCO_OPENAI_BASE_PATH = 'OCO_OPENAI_BASE_PATH',
|
||||
OCO_DESCRIPTION = 'OCO_DESCRIPTION',
|
||||
OCO_EMOJI = 'OCO_EMOJI',
|
||||
OCO_MODEL = 'OCO_MODEL',
|
||||
OCO_LANGUAGE = 'OCO_LANGUAGE'
|
||||
}
|
||||
|
||||
export enum CONFIG_MODES {
|
||||
@@ -36,15 +42,15 @@ const validateConfig = (
|
||||
};
|
||||
|
||||
export const configValidators = {
|
||||
[CONFIG_KEYS.OPENAI_API_KEY](value: any) {
|
||||
validateConfig(CONFIG_KEYS.OPENAI_API_KEY, value, 'Cannot be empty');
|
||||
[CONFIG_KEYS.OCO_OPENAI_API_KEY](value: any) {
|
||||
validateConfig(CONFIG_KEYS.OCO_OPENAI_API_KEY, value, 'Cannot be empty');
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OPENAI_API_KEY,
|
||||
CONFIG_KEYS.OCO_OPENAI_API_KEY,
|
||||
value.startsWith('sk-'),
|
||||
'Must start with "sk-"'
|
||||
);
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OPENAI_API_KEY,
|
||||
CONFIG_KEYS.OCO_OPENAI_API_KEY,
|
||||
value.length === 51,
|
||||
'Must be 51 characters long'
|
||||
);
|
||||
@@ -52,9 +58,9 @@ export const configValidators = {
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.description](value: any) {
|
||||
[CONFIG_KEYS.OCO_DESCRIPTION](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.description,
|
||||
CONFIG_KEYS.OCO_DESCRIPTION,
|
||||
typeof value === 'boolean',
|
||||
'Must be true or false'
|
||||
);
|
||||
@@ -62,9 +68,28 @@ export const configValidators = {
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.emoji](value: any) {
|
||||
[CONFIG_KEYS.OCO_OPENAI_MAX_TOKENS](value: any) {
|
||||
// If the value is a string, convert it to a number.
|
||||
if (typeof value === 'string') {
|
||||
value = parseInt(value);
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_OPENAI_MAX_TOKENS,
|
||||
!isNaN(value),
|
||||
'Must be a number'
|
||||
);
|
||||
}
|
||||
validateConfig(
|
||||
CONFIG_KEYS.emoji,
|
||||
CONFIG_KEYS.OCO_OPENAI_MAX_TOKENS,
|
||||
typeof value === 'number',
|
||||
'Must be a number'
|
||||
);
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_EMOJI](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_EMOJI,
|
||||
typeof value === 'boolean',
|
||||
'Must be true or false'
|
||||
);
|
||||
@@ -72,20 +97,29 @@ export const configValidators = {
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.language](value: any) {
|
||||
[CONFIG_KEYS.OCO_LANGUAGE](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.language,
|
||||
CONFIG_KEYS.OCO_LANGUAGE,
|
||||
getI18nLocal(value),
|
||||
`${value} is not supported yet`
|
||||
);
|
||||
return getI18nLocal(value);
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OPENAI_BASE_PATH](value: any) {
|
||||
[CONFIG_KEYS.OCO_OPENAI_BASE_PATH](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OPENAI_BASE_PATH,
|
||||
typeof value == 'string',
|
||||
`${value} is not supported yet`
|
||||
CONFIG_KEYS.OCO_OPENAI_BASE_PATH,
|
||||
typeof value === 'string',
|
||||
'Must be string'
|
||||
);
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_MODEL](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_OPENAI_BASE_PATH,
|
||||
value === 'gpt-3.5-turbo' || value === 'gpt-4',
|
||||
`${value} is not supported yet, use 'gpt-4' or 'gpt-3.5-turbo' (default)`
|
||||
);
|
||||
return value;
|
||||
}
|
||||
@@ -98,18 +132,39 @@ export type ConfigType = {
|
||||
const configPath = pathJoin(homedir(), '.opencommit');
|
||||
|
||||
export const getConfig = (): ConfigType | null => {
|
||||
const configFromEnv = {
|
||||
OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY,
|
||||
OCO_OPENAI_MAX_TOKENS: Number(process.env.OCO_OPENAI_MAX_TOKENS),
|
||||
OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH,
|
||||
OCO_DESCRIPTION: process.env.OCO_DESCRIPTION === 'true' ? true : false,
|
||||
OCO_EMOJI: process.env.OCO_EMOJI === 'true' ? true : false,
|
||||
OCO_MODEL: process.env.OCO_MODEL,
|
||||
OCO_LANGUAGE: process.env.OCO_LANGUAGE
|
||||
};
|
||||
|
||||
const configExists = existsSync(configPath);
|
||||
if (!configExists) return null;
|
||||
if (!configExists) return configFromEnv;
|
||||
|
||||
const configFile = readFileSync(configPath, 'utf8');
|
||||
const config = iniParse(configFile);
|
||||
|
||||
for (const configKey of Object.keys(config)) {
|
||||
const validValue = configValidators[configKey as CONFIG_KEYS](
|
||||
config[configKey]
|
||||
);
|
||||
try {
|
||||
const validator = configValidators[configKey as CONFIG_KEYS];
|
||||
const validValue = validator(
|
||||
config[configKey] ?? configFromEnv[configKey as CONFIG_KEYS]
|
||||
);
|
||||
|
||||
config[configKey] = validValue;
|
||||
config[configKey] = validValue;
|
||||
} catch (error) {
|
||||
outro(
|
||||
`'${configKey}' name is invalid, it should be either 'OCO_${configKey.toUpperCase()}' or it doesn't exist.`
|
||||
);
|
||||
outro(
|
||||
`Manually fix the '.env' file or global '~/.opencommit' config file.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
@@ -138,7 +193,7 @@ export const setConfig = (keyValues: [key: string, value: string][]) => {
|
||||
|
||||
writeFileSync(configPath, iniStringify(config), 'utf8');
|
||||
|
||||
outro(`${chalk.green('✔')} config successfully set`);
|
||||
outro(`${chalk.green('✔')} Config successfully set`);
|
||||
};
|
||||
|
||||
export const configCommand = command(
|
||||
|
||||
@@ -92,7 +92,7 @@ export const hookCommand = command(
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`unsupported mode: ${mode}. Supported modes are: 'set' or 'unset'`
|
||||
`Unsupported mode: ${mode}. Supported modes are: 'set' or 'unset'`
|
||||
);
|
||||
} catch (error) {
|
||||
outro(`${chalk.red('✖')} ${error}`);
|
||||
|
||||
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
||||
import { intro, outro, spinner } from '@clack/prompts';
|
||||
import { getChangedFiles, getDiff, getStagedFiles, gitAdd } from '../utils/git';
|
||||
import { getConfig } from './config';
|
||||
import { generateCommitMessageWithChatCompletion } from '../generateCommitMessageFromGitDiff';
|
||||
import { generateCommitMessageByDiff } from '../generateCommitMessageFromGitDiff';
|
||||
|
||||
const [messageFilePath, commitSource] = process.argv.slice(2);
|
||||
|
||||
@@ -37,7 +37,7 @@ export const prepareCommitMessageHook = async (
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
if (!config?.OPENAI_API_KEY) {
|
||||
if (!config?.OCO_OPENAI_API_KEY) {
|
||||
throw new Error(
|
||||
'No OPEN_AI_API exists. Set your OPEN_AI_API=<key> in ~/.opencommit'
|
||||
);
|
||||
@@ -45,13 +45,11 @@ export const prepareCommitMessageHook = async (
|
||||
|
||||
const spin = spinner();
|
||||
spin.start('Generating commit message');
|
||||
const commitMessage = await generateCommitMessageWithChatCompletion(
|
||||
|
||||
const commitMessage = await generateCommitMessageByDiff(
|
||||
await getDiff({ files: staged })
|
||||
);
|
||||
if (typeof commitMessage !== 'string') {
|
||||
spin.stop('Error');
|
||||
throw new Error(commitMessage.error);
|
||||
} else spin.stop('Done');
|
||||
spin.stop('Done');
|
||||
|
||||
const fileContent = await fs.readFile(messageFilePath);
|
||||
|
||||
|
||||
@@ -9,15 +9,15 @@ import { i18n, I18nLocals } from './i18n';
|
||||
import { tokenCount } from './utils/tokenCount';
|
||||
|
||||
const config = getConfig();
|
||||
const translation = i18n[(config?.language as I18nLocals) || 'en'];
|
||||
const translation = i18n[(config?.OCO_LANGUAGE as I18nLocals) || 'en'];
|
||||
|
||||
const INIT_MESSAGES_PROMPT: Array<ChatCompletionRequestMessage> = [
|
||||
{
|
||||
role: ChatCompletionRequestMessageRoleEnum.System,
|
||||
// prettier-ignore
|
||||
content: `You are to act as the author of a commit message in git. Your mission is to create clean and comprehensive commit messages in the conventional commit convention and explain why a change was done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message.
|
||||
${config?.emoji? 'Use GitMoji convention to preface the commit.': 'Do not preface the commit with anything.'}
|
||||
${config?.description ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.': "Don't add any descriptions to the commit, only commit message."}
|
||||
content: `You are to act as the author of a commit message in git. Your mission is to create clean and comprehensive commit messages in the conventional commit convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message.
|
||||
${config?.OCO_EMOJI ? 'Use GitMoji convention to preface the commit.': 'Do not preface the commit with anything.'}
|
||||
${config?.OCO_DESCRIPTION ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.': "Don't add any descriptions to the commit, only commit message."}
|
||||
Use the present tense. Lines must not be longer than 74 characters. Use ${translation.localLanguage} to answer.`
|
||||
},
|
||||
{
|
||||
@@ -49,9 +49,9 @@ app.use((_, res, next) => {
|
||||
},
|
||||
{
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
content: `${config?.emoji ? '🐛 ' : ''}${translation.commitFix}
|
||||
${config?.emoji ? '✨ ' : ''}${translation.commitFeat}
|
||||
${config?.description ? translation.commitDescription : ''}`
|
||||
content: `${config?.OCO_EMOJI ? '🐛 ' : ''}${translation.commitFix}
|
||||
${config?.OCO_EMOJI ? '✨ ' : ''}${translation.commitFeat}
|
||||
${config?.OCO_DESCRIPTION ? translation.commitDescription : ''}`
|
||||
}
|
||||
];
|
||||
|
||||
@@ -82,11 +82,11 @@ const INIT_MESSAGES_PROMPT_LENGTH = INIT_MESSAGES_PROMPT.map(
|
||||
(msg) => tokenCount(msg.content) + 4
|
||||
).reduce((a, b) => a + b, 0);
|
||||
|
||||
const MAX_REQ_TOKENS = 3900 - INIT_MESSAGES_PROMPT_LENGTH;
|
||||
const MAX_REQ_TOKENS = 3000 - INIT_MESSAGES_PROMPT_LENGTH;
|
||||
|
||||
export const generateCommitMessageWithChatCompletion = async (
|
||||
export const generateCommitMessageByDiff = async (
|
||||
diff: string
|
||||
): Promise<string | GenerateCommitMessageError> => {
|
||||
): Promise<string> => {
|
||||
try {
|
||||
if (tokenCount(diff) >= MAX_REQ_TOKENS) {
|
||||
const commitMessagePromises = getCommitMsgsPromisesFromFileDiffs(
|
||||
@@ -103,12 +103,12 @@ export const generateCommitMessageWithChatCompletion = async (
|
||||
const commitMessage = await api.generateCommitMessage(messages);
|
||||
|
||||
if (!commitMessage)
|
||||
return { error: GenerateCommitMessageErrorEnum.emptyMessage };
|
||||
throw new Error(GenerateCommitMessageErrorEnum.emptyMessage);
|
||||
|
||||
return commitMessage;
|
||||
}
|
||||
} catch (error) {
|
||||
return { error: GenerateCommitMessageErrorEnum.internalError };
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
216
src/github-action.ts
Normal file
216
src/github-action.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import core from '@actions/core';
|
||||
import github from '@actions/github';
|
||||
import exec from '@actions/exec';
|
||||
import { intro, outro } from '@clack/prompts';
|
||||
import { PushEvent } from '@octokit/webhooks-types';
|
||||
import { generateCommitMessageByDiff } from './generateCommitMessageFromGitDiff';
|
||||
import { sleep } from './utils/sleep';
|
||||
import { randomIntFromInterval } from './utils/randomIntFromInterval';
|
||||
import { unlinkSync, writeFileSync } from 'fs';
|
||||
|
||||
// This should be a token with access to your repository scoped in as a secret.
|
||||
// The YML workflow will need to set GITHUB_TOKEN with the GitHub Secret Token
|
||||
// GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
// https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret
|
||||
const GITHUB_TOKEN = core.getInput('GITHUB_TOKEN');
|
||||
const octokit = github.getOctokit(GITHUB_TOKEN);
|
||||
const context = github.context;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
type SHA = string;
|
||||
type Diff = string;
|
||||
|
||||
async function getCommitDiff(commitSha: string) {
|
||||
const diffResponse = await octokit.request<string>(
|
||||
'GET /repos/{owner}/{repo}/commits/{ref}',
|
||||
{
|
||||
owner,
|
||||
repo,
|
||||
ref: commitSha,
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3.diff'
|
||||
}
|
||||
}
|
||||
);
|
||||
return { sha: commitSha, diff: diffResponse.data };
|
||||
}
|
||||
|
||||
interface DiffAndSHA {
|
||||
sha: SHA;
|
||||
diff: Diff;
|
||||
}
|
||||
|
||||
interface MsgAndSHA {
|
||||
sha: SHA;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
// send only 3-4 size chunks of diffs in steps,
|
||||
// because openAI restricts "too many requests" at once with 429 error
|
||||
async function improveMessagesInChunks(diffsAndSHAs: DiffAndSHA[]) {
|
||||
const chunkSize = diffsAndSHAs!.length % 2 === 0 ? 4 : 3;
|
||||
outro(`Improving commit messages in chunks of ${chunkSize}.`);
|
||||
const improvePromises = diffsAndSHAs!.map((commit) =>
|
||||
generateCommitMessageByDiff(commit.diff)
|
||||
);
|
||||
|
||||
let improvedMessagesAndSHAs: MsgAndSHA[] = [];
|
||||
for (let step = 0; step < improvePromises.length; step += chunkSize) {
|
||||
const chunkOfPromises = improvePromises.slice(step, step + chunkSize);
|
||||
|
||||
try {
|
||||
const chunkOfImprovedMessages = await Promise.all(chunkOfPromises);
|
||||
|
||||
const chunkOfImprovedMessagesBySha = chunkOfImprovedMessages.map(
|
||||
(improvedMsg, i) => {
|
||||
const index = improvedMessagesAndSHAs.length;
|
||||
const sha = diffsAndSHAs![index + i].sha;
|
||||
|
||||
return { sha, msg: improvedMsg };
|
||||
}
|
||||
);
|
||||
|
||||
improvedMessagesAndSHAs.push(...chunkOfImprovedMessagesBySha);
|
||||
|
||||
// sometimes openAI errors with 429 code (too many requests),
|
||||
// so lets sleep a bit
|
||||
const sleepFor =
|
||||
1000 * randomIntFromInterval(1, 5) + 100 * randomIntFromInterval(1, 5);
|
||||
|
||||
outro(
|
||||
`Improved ${chunkOfPromises.length} messages. Sleeping for ${sleepFor}`
|
||||
);
|
||||
|
||||
await sleep(sleepFor);
|
||||
} catch (error) {
|
||||
outro(error as string);
|
||||
|
||||
// if sleeping in try block still fails with 429,
|
||||
// openAI wants at least 1 minute before next request
|
||||
const sleepFor = 60000 + 1000 * randomIntFromInterval(1, 5);
|
||||
outro(`Retrying after sleeping for ${sleepFor}`);
|
||||
await sleep(sleepFor);
|
||||
|
||||
// go to previous step
|
||||
step -= chunkSize;
|
||||
}
|
||||
}
|
||||
|
||||
return improvedMessagesAndSHAs;
|
||||
}
|
||||
|
||||
const getDiffsBySHAs = async (SHAs: string[]) => {
|
||||
const diffPromises = SHAs.map((sha) => getCommitDiff(sha));
|
||||
|
||||
const diffs = await Promise.all(diffPromises).catch((error) => {
|
||||
outro(`Error in Promise.all(getCommitDiffs(SHAs)): ${error}.`);
|
||||
throw error;
|
||||
});
|
||||
|
||||
return diffs;
|
||||
};
|
||||
|
||||
async function improveCommitMessages(
|
||||
commitsToImprove: { id: string; message: string }[]
|
||||
): Promise<void> {
|
||||
if (commitsToImprove.length) {
|
||||
outro(`Found ${commitsToImprove.length} commits to improve.`);
|
||||
} else {
|
||||
outro('No new commits found.');
|
||||
return;
|
||||
}
|
||||
|
||||
outro('Fetching commit diffs by SHAs.');
|
||||
const commitSHAsToImprove = commitsToImprove.map((commit) => commit.id);
|
||||
const diffsWithSHAs = await getDiffsBySHAs(commitSHAsToImprove);
|
||||
outro('Done.');
|
||||
|
||||
const improvedMessagesWithSHAs = await improveMessagesInChunks(diffsWithSHAs);
|
||||
|
||||
console.log(
|
||||
`Improved ${improvedMessagesWithSHAs.length} commits: `,
|
||||
improvedMessagesWithSHAs
|
||||
);
|
||||
|
||||
const createCommitMessageFile = (message: string, index: number) =>
|
||||
writeFileSync(`./commit-${index}.txt`, message);
|
||||
improvedMessagesWithSHAs.forEach(({ msg }, i) =>
|
||||
createCommitMessageFile(msg, i)
|
||||
);
|
||||
|
||||
writeFileSync(`./count.txt`, '0');
|
||||
|
||||
writeFileSync(
|
||||
'./rebase-exec.sh',
|
||||
`#!/bin/bash
|
||||
count=$(cat count.txt)
|
||||
git commit --amend -F commit-$count.txt
|
||||
echo $(( count + 1 )) > count.txt`
|
||||
);
|
||||
|
||||
await exec.exec(`chmod +x ./rebase-exec.sh`);
|
||||
|
||||
await exec.exec(
|
||||
'git',
|
||||
['rebase', `${commitsToImprove[0].id}^`, '--exec', './rebase-exec.sh'],
|
||||
{
|
||||
env: {
|
||||
GIT_SEQUENCE_EDITOR: 'sed -i -e "s/^pick/reword/g"',
|
||||
GIT_COMMITTER_NAME: process.env.GITHUB_ACTOR!,
|
||||
GIT_COMMITTER_EMAIL: `${process.env.GITHUB_ACTOR}@users.noreply.github.com`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const deleteCommitMessageFile = (index: number) =>
|
||||
unlinkSync(`./commit-${index}.txt`);
|
||||
commitsToImprove.forEach((_commit, i) => deleteCommitMessageFile(i));
|
||||
|
||||
unlinkSync('./count.txt');
|
||||
unlinkSync('./rebase-exec.sh');
|
||||
|
||||
outro('Force pushing non-interactively rebased commits into remote.');
|
||||
|
||||
await exec.exec('git', ['status']);
|
||||
|
||||
// Force push the rebased commits
|
||||
await exec.exec('git', ['push', `--force`]);
|
||||
|
||||
outro('Done 🧙');
|
||||
}
|
||||
|
||||
async function run() {
|
||||
intro('OpenCommit — improving lame commit messages');
|
||||
|
||||
try {
|
||||
if (github.context.eventName === 'push') {
|
||||
outro(`Processing commits in a Push event`);
|
||||
|
||||
const payload = github.context.payload as PushEvent;
|
||||
|
||||
const commits = payload.commits;
|
||||
|
||||
// Set local Git user identity for future git history manipulations
|
||||
if (payload.pusher.email)
|
||||
await exec.exec('git', ['config', 'user.email', payload.pusher.email]);
|
||||
|
||||
await exec.exec('git', ['config', 'user.name', payload.pusher.name]);
|
||||
|
||||
await exec.exec('git', ['status']);
|
||||
await exec.exec('git', ['log', '--oneline']);
|
||||
|
||||
await improveCommitMessages(commits);
|
||||
} else {
|
||||
outro('Wrong action.');
|
||||
core.error(
|
||||
`OpenCommit was called on ${github.context.payload.action}. OpenCommit is supposed to be used on "push" action.`
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
const err = error?.message || error;
|
||||
core.setFailed(err);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
6
src/i18n/cs.json
Normal file
6
src/i18n/cs.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "česky",
|
||||
"commitFix": "fix(server.ts): zlepšení velikosti proměnné port na velká písmena PORT",
|
||||
"commitFeat": "feat(server.ts): přidání podpory pro proměnnou prostředí process.env.PORT",
|
||||
"commitDescription": "Proměnná port se nyní jmenuje PORT, což odpovídá konvenci pojmenování, protože PORT je konstanta. Podpora proměnné prostředí process.env.PORT umožňuje snadnější správu nastavení při spuštění."
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"localLanguage": "english",
|
||||
"commitFix": "fix(server.ts): change port variable case from lowercase port to uppercase PORT",
|
||||
"commitFeat": "feat(server.ts): add support for process.env.PORT environment variable",
|
||||
"commitFix": "fix(server.ts): change port variable case from lowercase port to uppercase PORT to improve semantics",
|
||||
"commitFeat": "feat(server.ts): add support for process.env.PORT environment variable to be able to run app on a configurable port",
|
||||
"commitDescription": "The port variable is now named PORT, which improves consistency with the naming conventions as PORT is a constant. Support for an environment variable allows the application to be more flexible as it can now run on any available port specified via the process.env.PORT environment variable."
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import en from '../i18n/en.json' assert { type: 'json' };
|
||||
import cs from '../i18n/cs.json' assert { type: 'json' };
|
||||
import de from '../i18n/de.json' assert { type: 'json' };
|
||||
import fr from '../i18n/fr.json' assert { type: 'json' };
|
||||
import it from '../i18n/it.json' assert { type: 'json' };
|
||||
@@ -14,6 +15,7 @@ import nl from '../i18n/nl.json' assert { type: 'json' };
|
||||
import ru from '../i18n/ru.json' assert { type: 'json' };
|
||||
import id_ID from '../i18n/id_ID.json' assert { type: 'json' };
|
||||
import pl from '../i18n/pl.json' assert { type: 'json' };
|
||||
import tr from '../i18n/tr.json' assert { type: 'json' };
|
||||
import th from '../i18n/th.json' assert { type: 'json' };
|
||||
|
||||
export enum I18nLocals {
|
||||
@@ -21,6 +23,7 @@ export enum I18nLocals {
|
||||
'zh_CN' = 'zh_CN',
|
||||
'zh_TW' = 'zh_TW',
|
||||
'ja' = 'ja',
|
||||
'cs' = 'cs',
|
||||
'de' = 'de',
|
||||
'fr' = 'fr',
|
||||
'nl' = 'nl',
|
||||
@@ -32,6 +35,7 @@ export enum I18nLocals {
|
||||
'ru' = 'ru',
|
||||
'id_ID' = 'id_ID',
|
||||
'pl' = 'pl',
|
||||
'tr' = 'tr',
|
||||
'th' = 'th',
|
||||
}
|
||||
|
||||
@@ -40,6 +44,7 @@ export const i18n = {
|
||||
zh_CN,
|
||||
zh_TW,
|
||||
ja,
|
||||
cs,
|
||||
de,
|
||||
fr,
|
||||
it,
|
||||
@@ -52,6 +57,7 @@ export const i18n = {
|
||||
nl,
|
||||
ru,
|
||||
pl,
|
||||
tr,
|
||||
th
|
||||
};
|
||||
|
||||
@@ -60,6 +66,7 @@ export const I18N_CONFIG_ALIAS: { [key: string]: string[] } = {
|
||||
zh_TW: ['zh_TW', '繁體中文', '繁體'],
|
||||
ja: ['ja', 'Japanese', 'にほんご'],
|
||||
ko: ['ko', 'Korean', '한국어'],
|
||||
cs: ['cs', 'Czech', 'česky'],
|
||||
de: ['de', 'German', 'Deutsch'],
|
||||
fr: ['fr', 'French', 'française'],
|
||||
it: ['it', 'Italian', 'italiano'],
|
||||
@@ -72,6 +79,7 @@ export const I18N_CONFIG_ALIAS: { [key: string]: string[] } = {
|
||||
ru: ['ru', 'Russian', 'русский'],
|
||||
id_ID: ['id_ID', 'Bahasa', 'bahasa'],
|
||||
pl: ['pl', 'Polish', 'Polski'],
|
||||
tr: ['tr', 'Turkish', 'Turkish'],
|
||||
th: ['th', 'Thai', 'ไทย']
|
||||
};
|
||||
|
||||
|
||||
6
src/i18n/tr.json
Normal file
6
src/i18n/tr.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"localLanguage": "Turkish",
|
||||
"commitFix": "fix(server.ts): port değişkeni küçük harfli porttan büyük harfli PORT'a değiştirildi",
|
||||
"commitFeat": "feat(server.ts): process.env.PORT ortam değişkeni için destek eklendi.",
|
||||
"commitDescription": "Bağlantı noktası değişkeni artık PORT olarak adlandırıldı ve PORT bir sabit değişken olduğu için bu adlandırma tutarlılığı artırır. Ortam değişkeni desteği, artık process.env.PORT ortam değişkeni aracılığıyla belirtilen herhangi bir kullanılabilir bağlantı noktasında çalışabileceğinden uygulamanın daha esnek olmasını sağlar."
|
||||
}
|
||||
4
src/utils/randomIntFromInterval.ts
Normal file
4
src/utils/randomIntFromInterval.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export function randomIntFromInterval(min: number, max: number) {
|
||||
// min and max included
|
||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||
}
|
||||
3
src/utils/sleep.ts
Normal file
3
src/utils/sleep.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
"module": "ESNext",
|
||||
// "rootDir": "./src",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node",
|
||||
|
||||
"allowJs": true,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user