From f7e4f09d4ebf543325582c7ccfd79e26a6c63527 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Sat, 20 Jun 2026 13:15:44 +0000 Subject: [PATCH] =?UTF-8?q?feat(OpenCode=20TLS):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=87=AA=E7=B0=BD=E6=86=91=E8=AD=89=E9=A9=97=E8=AD=89=E7=95=A5?= =?UTF-8?q?=E9=81=8E=E8=A8=AD=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- action.yaml | 5 +++++ app/config.js | 4 ++++ app/llm.js | 14 +++++++++++--- app/preflight.js | 10 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/action.yaml b/action.yaml index 0d1e7cf..30b6eb4 100644 --- a/action.yaml +++ b/action.yaml @@ -95,6 +95,10 @@ inputs: OPENCODE_SERVER_PASSWORD: description: 'OpenCode server Basic Auth password' required: false + OPENCODE_SKIP_TLS_VERIFY: + description: '跳過 OpenCode server SSL/TLS 憑證驗證(自簽憑證時使用)' + required: false + default: 'false' runs: using: 'docker' @@ -129,3 +133,4 @@ runs: OPENCODE_PROVIDER: ${{ inputs.OPENCODE_PROVIDER }} OPENCODE_SERVER_USERNAME: ${{ inputs.OPENCODE_SERVER_USERNAME }} OPENCODE_SERVER_PASSWORD: ${{ inputs.OPENCODE_SERVER_PASSWORD }} + OPENCODE_SKIP_TLS_VERIFY: ${{ inputs.OPENCODE_SKIP_TLS_VERIFY }} diff --git a/app/config.js b/app/config.js index e2e0c6f..9ba433f 100644 --- a/app/config.js +++ b/app/config.js @@ -11,6 +11,10 @@ export const PR_BASE_BRANCH = process.env.PR_BASE_BRANCH || ''; export const FINDINGS_PATH = '.gitea/ai-review/findings.json'; export const EXCLUSIONS_PATH = '.gitea/ai-review/exclusions.json'; +export function shouldSkipOpenCodeTLSVerify() { + return process.env.OPENCODE_SKIP_TLS_VERIFY === 'true'; +} + /** 將逗號分隔的 API key 字串拆成陣列 */ function splitKeys(value) { if (!value) return []; diff --git a/app/llm.js b/app/llm.js index 12a0dd7..d4e6214 100644 --- a/app/llm.js +++ b/app/llm.js @@ -1,5 +1,6 @@ import axios from 'axios'; -import { getLLMConfig } from './config.js'; +import https from 'https'; +import { getLLMConfig, shouldSkipOpenCodeTLSVerify } from './config.js'; import { line, error } from './log.js'; function isOpenAIGpt55(provider, model) { @@ -46,6 +47,13 @@ function applyOpenCodeAuth(headers) { headers['Authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; } +function opencodeAxiosOptions(headers) { + return { + headers, + httpsAgent: shouldSkipOpenCodeTLSVerify() ? new https.Agent({ rejectUnauthorized: false }) : undefined, + }; +} + function extractOpenCodeContent(data) { const parts = data.parts || data.data?.parts || data.info?.content || data.data?.info?.content || []; return parts @@ -60,7 +68,7 @@ async function chatOpenCode(baseURL, model, systemPrompt, userContent, headers) const session = await axios.post( `${base}/session`, { title: 'AI Code Review', model: { providerID, id: modelID } }, - { headers } + opencodeAxiosOptions(headers) ); const sessionID = session.data.id || session.data.data?.id; if (!sessionID) throw new Error('OpenCode session 建立失敗:回應中沒有 session id'); @@ -72,7 +80,7 @@ async function chatOpenCode(baseURL, model, systemPrompt, userContent, headers) system: systemPrompt, parts: [{ type: 'text', text: userContent }], }, - { headers } + opencodeAxiosOptions(headers) ); return extractOpenCodeContent(resp.data); } diff --git a/app/preflight.js b/app/preflight.js index bcaa394..6690bbe 100644 --- a/app/preflight.js +++ b/app/preflight.js @@ -8,6 +8,7 @@ import { GITEA_SKIP_TLS_VERIFY, PR_NUMBER, getLLMConfig, + shouldSkipOpenCodeTLSVerify, } from './config.js'; import { verifyRemoteAccess } from './git.js'; import { step, line, ok, error } from './log.js'; @@ -26,6 +27,11 @@ const applyOpenCodeAuth = (headers) => { const username = process.env.OPENCODE_SERVER_USERNAME || 'opencode'; headers['Authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; }; +const opencodeAxiosOptions = (headers) => ({ + headers, + timeout: 30000, + httpsAgent: shouldSkipOpenCodeTLSVerify() ? new https.Agent({ rejectUnauthorized: false }) : undefined, +}); function giteaErr(e) { const status = e.response?.status; @@ -89,8 +95,8 @@ export async function verifyLLM() { const { providerID, modelID } = opencodeModelConfig(model); applyOpenCodeAuth(headers); try { - await axios.get(`${base}/global/health`, { headers, timeout: 30000 }); - const providers = await axios.get(`${base}/config/providers`, { headers, timeout: 30000 }); + await axios.get(`${base}/global/health`, opencodeAxiosOptions(headers)); + const providers = await axios.get(`${base}/config/providers`, opencodeAxiosOptions(headers)); const configuredProvider = providers.data.providers?.find(p => p.id === providerID); if (!configuredProvider) return { ok: false, provider, error: `OpenCode server 未設定 provider=${providerID}` }; if (!configuredProvider.models?.[modelID]) return { ok: false, provider, error: `OpenCode server provider=${providerID} 未列出 model=${modelID}` };