feat: HTTP + HTTPS proxy support (#139)
Co-authored-by: Hiroki Osame <hiroki.osame@gmail.com>
This commit is contained in:
10
README.md
10
README.md
@@ -165,7 +165,15 @@ The number of commit messages to generate to pick from.
|
||||
|
||||
Note, this will use more tokens as it generates more results.
|
||||
|
||||
This can also be configured with the CLI flag `--generate`.
|
||||
#### proxy
|
||||
|
||||
Set a HTTP/HTTPS proxy to use for requests.
|
||||
|
||||
To clear the proxy option, you can use the command (note the empty value after the equals sign):
|
||||
|
||||
```sh
|
||||
aicommits config set proxy=
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"eslint": "^8.35.0",
|
||||
"execa": "^7.0.0",
|
||||
"fs-fixture": "^1.2.0",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"ini": "^3.0.1",
|
||||
"kolorist": "^1.7.0",
|
||||
"lint-staged": "^13.1.2",
|
||||
|
||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@@ -17,6 +17,7 @@ specifiers:
|
||||
eslint: ^8.35.0
|
||||
execa: ^7.0.0
|
||||
fs-fixture: ^1.2.0
|
||||
https-proxy-agent: ^5.0.1
|
||||
ini: ^3.0.1
|
||||
kolorist: ^1.7.0
|
||||
lint-staged: ^13.1.2
|
||||
@@ -41,6 +42,7 @@ devDependencies:
|
||||
eslint: 8.35.0
|
||||
execa: 7.0.0
|
||||
fs-fixture: 1.2.0
|
||||
https-proxy-agent: 5.0.1
|
||||
ini: 3.0.1
|
||||
kolorist: 1.7.0
|
||||
lint-staged: 13.1.2
|
||||
@@ -787,6 +789,15 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/agent-base/6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/aggregate-error/3.1.0:
|
||||
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -2074,6 +2085,16 @@ packages:
|
||||
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
||||
dev: true
|
||||
|
||||
/https-proxy-agent/5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/human-signals/3.0.1:
|
||||
resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
@@ -35,8 +35,10 @@ export default async (
|
||||
staged.files.map(file => ` ${file}`).join('\n')
|
||||
}`);
|
||||
|
||||
const { env } = process;
|
||||
const config = await getConfig({
|
||||
OPENAI_KEY: process.env.OPENAI_KEY ?? process.env.OPENAI_API_KEY,
|
||||
OPENAI_KEY: env.OPENAI_KEY || env.OPENAI_API_KEY,
|
||||
proxy: env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY,
|
||||
generate: generate?.toString(),
|
||||
});
|
||||
|
||||
@@ -49,6 +51,7 @@ export default async (
|
||||
config.locale,
|
||||
staged.diff,
|
||||
config.generate,
|
||||
config.proxy,
|
||||
);
|
||||
} finally {
|
||||
s.stop('Changes analyzed');
|
||||
|
||||
@@ -30,7 +30,10 @@ export default () => (async () => {
|
||||
|
||||
intro(bgCyan(black(' aicommits ')));
|
||||
|
||||
const config = await getConfig();
|
||||
const { env } = process;
|
||||
const config = await getConfig({
|
||||
proxy: env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY,
|
||||
});
|
||||
|
||||
const s = spinner();
|
||||
s.start('The AI is analyzing your changes');
|
||||
@@ -41,6 +44,7 @@ export default () => (async () => {
|
||||
config.locale,
|
||||
staged!.diff,
|
||||
config.generate,
|
||||
config.proxy,
|
||||
);
|
||||
} finally {
|
||||
s.stop('Changes analyzed');
|
||||
|
||||
@@ -51,6 +51,15 @@ const configParsers = {
|
||||
|
||||
return parsed;
|
||||
},
|
||||
proxy(url?: string) {
|
||||
if (!url || url.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
parseAssert('proxy', /^https?:\/\//.test(url), 'Must be a valid URL');
|
||||
|
||||
return url;
|
||||
},
|
||||
} as const;
|
||||
|
||||
type ConfigKeys = keyof typeof configParsers;
|
||||
|
||||
@@ -2,6 +2,7 @@ import https from 'https';
|
||||
import type { ClientRequest, IncomingMessage } from 'http';
|
||||
import type { CreateChatCompletionRequest, CreateChatCompletionResponse } from 'openai';
|
||||
import { encoding_for_model as encodingForModel } from '@dqbd/tiktoken';
|
||||
import createHttpsProxyAgent from 'https-proxy-agent';
|
||||
import { KnownError } from './error.js';
|
||||
|
||||
const httpsPost = async (
|
||||
@@ -9,6 +10,7 @@ const httpsPost = async (
|
||||
path: string,
|
||||
headers: Record<string, string>,
|
||||
json: unknown,
|
||||
proxy?: string,
|
||||
) => new Promise<{
|
||||
request: ClientRequest;
|
||||
response: IncomingMessage;
|
||||
@@ -27,6 +29,11 @@ const httpsPost = async (
|
||||
'Content-Length': Buffer.byteLength(postContent),
|
||||
},
|
||||
timeout: 10_000, // 10s
|
||||
agent: (
|
||||
proxy
|
||||
? createHttpsProxyAgent(proxy)
|
||||
: undefined
|
||||
),
|
||||
},
|
||||
(response) => {
|
||||
const body: Buffer[] = [];
|
||||
@@ -53,6 +60,7 @@ const httpsPost = async (
|
||||
const createChatCompletion = async (
|
||||
apiKey: string,
|
||||
json: CreateChatCompletionRequest,
|
||||
proxy?: string,
|
||||
) => {
|
||||
const { response, data } = await httpsPost(
|
||||
'api.openai.com',
|
||||
@@ -61,6 +69,7 @@ const createChatCompletion = async (
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
json,
|
||||
proxy,
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -97,6 +106,7 @@ export const generateCommitMessage = async (
|
||||
locale: string,
|
||||
diff: string,
|
||||
completions: number,
|
||||
proxy?: string,
|
||||
) => {
|
||||
const prompt = getPrompt(locale, diff);
|
||||
|
||||
@@ -109,20 +119,24 @@ export const generateCommitMessage = async (
|
||||
}
|
||||
|
||||
try {
|
||||
const completion = await createChatCompletion(apiKey, {
|
||||
model,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
}],
|
||||
temperature: 0.7,
|
||||
top_p: 1,
|
||||
frequency_penalty: 0,
|
||||
presence_penalty: 0,
|
||||
max_tokens: 200,
|
||||
stream: false,
|
||||
n: completions,
|
||||
});
|
||||
const completion = await createChatCompletion(
|
||||
apiKey,
|
||||
{
|
||||
model,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
}],
|
||||
temperature: 0.7,
|
||||
top_p: 1,
|
||||
frequency_penalty: 0,
|
||||
presence_penalty: 0,
|
||||
max_tokens: 200,
|
||||
stream: false,
|
||||
n: completions,
|
||||
},
|
||||
proxy,
|
||||
);
|
||||
|
||||
return deduplicateMessages(
|
||||
completion.choices
|
||||
|
||||
Reference in New Issue
Block a user