feat: HTTP + HTTPS proxy support (#139)

Co-authored-by: Hiroki Osame <hiroki.osame@gmail.com>
This commit is contained in:
Sam Büth
2023-03-27 08:51:51 +02:00
committed by GitHub
parent 6e02dc8f81
commit a0db0f3ece
7 changed files with 77 additions and 17 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -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'}

View File

@@ -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');

View File

@@ -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');

View File

@@ -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;

View File

@@ -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,7 +119,9 @@ export const generateCommitMessage = async (
}
try {
const completion = await createChatCompletion(apiKey, {
const completion = await createChatCompletion(
apiKey,
{
model,
messages: [{
role: 'user',
@@ -122,7 +134,9 @@ export const generateCommitMessage = async (
max_tokens: 200,
stream: false,
n: completions,
});
},
proxy,
);
return deduplicateMessages(
completion.choices