refactor: switch ai-code-review to CLIProxyAPI
CI / 1. BUILD (pull_request) Successful in 3s
CI / 2. TEST (pull_request) Skipped
CI / 3. RESULT (pull_request) Skipped

This commit is contained in:
2026-08-06 17:42:49 +00:00
parent dc82b3c6d3
commit 21687fd2bd
11 changed files with 341 additions and 543 deletions
+19 -73
View File
@@ -1,6 +1,5 @@
import https from 'https';
import fs from 'fs';
import { execFileSync } from 'child_process';
// 本 action 會連接自架 Gitea / OpenCode,部署環境可能使用內部 CA 或自簽憑證。
// 對外部服務請優先使用預設 TLS 驗證;需要內部服務相容時才使用 getInsecureHttpsAgent()。
@@ -37,6 +36,9 @@ export const PR_NUMBER = process.env.PR_NUMBER || (PR.number != null ? String(PR
export const PR_HEAD_SHA = process.env.PR_HEAD_SHA || PR.head?.sha || process.env.GITHUB_SHA || '';
export const PR_HEAD_BRANCH = process.env.PR_HEAD_BRANCH || PR.head?.ref || process.env.GITHUB_HEAD_REF || '';
export const PR_BASE_BRANCH = process.env.PR_BASE_BRANCH || PR.base?.ref || process.env.GITHUB_BASE_REF || '';
export const CLI_PROXY_API = process.env.INPUT_CLI_PROXY_API || process.env.CLI_PROXY_API || '';
export const CLI_PROXY_API_KEY = process.env.INPUT_CLI_PROXY_API_KEY || process.env.CLI_PROXY_API_KEY || '';
export const LLM_PROVIDER = 'cliproxyapi';
export const FINDINGS_PATH = '.gitea/ai-review/findings.json';
export const EXCLUSIONS_PATH = '.gitea/ai-review/exclusions.json';
@@ -64,82 +66,26 @@ export function getInsecureHttpsAgent() {
// 過渡別名:既有呼叫端仍可用 OpenCode 語意名稱;新程式碼請直接使用 getInsecureHttpsAgent。
export const getOpenCodeHttpsAgent = getInsecureHttpsAgent;
const CLI_CANDIDATES = [
{
provider: 'codex',
command: 'codex',
defaultModel: 'gpt-5.4-mini',
},
{
provider: 'claude',
command: 'claude',
defaultModel: 'sonnet',
},
{
provider: 'antigravity',
command: 'agy',
defaultModel: 'gemini-2.5-flash',
},
{
provider: 'antigravity',
command: 'antigravity',
defaultModel: 'gemini-2.5-flash',
},
{
provider: 'opencode',
command: 'opencode',
defaultModel: 'google/gemini-2.5-flash',
},
];
/**
* 取得目前支援的 LLM CLI 指令名稱清單。
* 依環境變數解析並回傳 CLIProxyAPI 設定。
*
* @remarks 內容直接取自 `CLI_CANDIDATES`,若日後候選清單增減,輸出會同步變動。
* 優先讀取 `INPUT_CLI_PROXY_API` / `CLI_PROXY_API` 作為 base URL,`INPUT_MODEL` / `MODEL`
* 作為模型名稱,`INPUT_CLI_PROXY_API_KEY` / `CLI_PROXY_API_KEY` 作為存取金鑰。
*
* @returns {{ provider: ('cliproxyapi'|null), apiKeys: string[], baseURL: (string|null), model: (string|null), command: null }}
* 設定物件;`provider` 為 `null` 表示沒有可用的 proxy 設定。
*/
export function getLLMCLICommands() {
return CLI_CANDIDATES.map(c => c.command);
}
/**
* 檢查指定 CLI 指令是否可在目前環境中執行。
*
* @param {string} command - 要檢查的指令名稱。
* @returns {boolean} 找得到指令時回傳 `true`,否則回傳 `false`。
* @remarks 透過 `/bin/sh -lc "command -v <command>"` 檢查,屬於同步存在性檢查。
*/
function commandExists(command) {
try {
execFileSync('/bin/sh', ['-lc', `command -v ${command}`], { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
/**
* 依環境變數解析並回傳 LLM 提供者設定。
*
* 優先使用 `AI_ASSISTANT_CLI` 指定的 CLI;未指定時依序偵測 codex、claude、antigravity、opencode。
* model 依序取 `with: model`(`INPUT_MODEL`)、`MODEL`、相容舊的 `OPENCODE_MODEL`,最後用各 CLI 預設值。
*
* @param {{ commandExistsFn?: (command: string) => boolean }} [deps] - 可注入的 CLI 偵測函式,供測試使用。
* @returns {{ provider: ('codex'|'claude'|'antigravity'|'opencode'|null), apiKeys: string[], baseURL: null, model: (string|null), command: (string|null) }}
* LLM 設定物件;`provider` 為 `null` 表示沒有可用的提供者。
*/
export function getLLMConfig({ commandExistsFn = commandExists } = {}) {
const requested = process.env.AI_ASSISTANT_CLI;
const candidates = requested
? CLI_CANDIDATES.filter(c => c.provider === requested || c.command === requested)
: CLI_CANDIDATES;
const cli = candidates.find(c => commandExistsFn(c.command));
if (!cli) return { provider: null, apiKeys: [], baseURL: null, model: null, command: null };
export function getLLMConfig() {
const baseURL = String(process.env.INPUT_CLI_PROXY_API || process.env.CLI_PROXY_API || '').trim().replace(/\/$/, '');
const model = process.env.INPUT_MODEL || process.env.MODEL || process.env.OPENCODE_MODEL || '';
const apiKey = String(process.env.INPUT_CLI_PROXY_API_KEY || process.env.CLI_PROXY_API_KEY || '').trim();
if (!baseURL) return { provider: null, apiKeys: [], baseURL: null, model: model || null, command: null };
return {
provider: cli.provider,
apiKeys: [cli.provider],
baseURL: null,
model: process.env.INPUT_MODEL || process.env.MODEL || process.env.OPENCODE_MODEL || cli.defaultModel,
command: cli.command,
provider: LLM_PROVIDER,
apiKeys: apiKey ? [apiKey] : [],
baseURL,
model: model || null,
command: null,
};
}