From 544937bb91761a9fcf6973db0587f2d368a06408 Mon Sep 17 00:00:00 2001 From: Jenkins CI Date: Thu, 23 Apr 2026 00:35:57 +0800 Subject: [PATCH] ci: auto build & obfuscate [2026-04-23 00:35:57] (Jenkins #17) --- CharacterWorldBook/src/cwb_apiService.js | 19 ++------ CharacterWorldBook/src/cwb_settingsManager.js | 2 - assets/rule-config-panel.html | 43 +++++++++++++++++-- core/amily2-updater.js | 5 ++- core/api.js | 17 +++++--- core/auto-char-card/agent-manager.js | 3 +- core/rag-api.js | 11 +++-- core/rag-processor.js | 19 ++++++-- core/utils/pollingManager.js | 7 ++- glossary/GT_bindings.js | 9 +++- ui/api-config-bindings.js | 8 ++-- ui/hanlinyuan-bindings.js | 28 ++++++------ ui/historiography-bindings.js | 10 ++--- ui/message-table-renderer.js | 12 +++--- ui/profile-sync.js | 3 +- ui/rule-config-bindings.js | 4 +- ui/table-bindings.js | 10 ++--- utils/auth.js | 2 +- utils/config/RuleProfileManager.js | 13 ++++++ utils/config/api-key-store/ApiKeyStore.js | 2 +- utils/config/api-key-store/crypto-utils.js | 2 +- utils/config/sensitive-keys.js | 2 +- utils/tagProcessor.js | 13 ++++-- 23 files changed, 160 insertions(+), 84 deletions(-) diff --git a/CharacterWorldBook/src/cwb_apiService.js b/CharacterWorldBook/src/cwb_apiService.js index b09a8fc..4092689 100644 --- a/CharacterWorldBook/src/cwb_apiService.js +++ b/CharacterWorldBook/src/cwb_apiService.js @@ -679,11 +679,6 @@ export async function callCustomOpenAI(messages) { const headers = { ...getRequestHeaders(), 'Content-Type': 'application/json' }; const body = JSON.stringify(requestBody); - console.groupCollapsed(`[CWB] API Call @ ${new Date().toLocaleTimeString()}`); - console.log('Request URL:', fullApiUrl); - console.log('Request Headers:', headers); - console.log('Request Body:', requestBody); - try { const response = await fetch(fullApiUrl, { method: 'POST', @@ -693,27 +688,19 @@ export async function callCustomOpenAI(messages) { if (!response.ok) { const errTxt = await response.text(); - console.error('API Error Response:', errTxt); throw new Error(`API请求失败: ${response.status} ${errTxt}`); } const data = await response.json(); - console.log('API Full Response:', data); - + if (data.choices && data.choices[0]?.message?.content) { - console.log('Extracted Content:', data.choices[0].message.content.trim()); - console.groupEnd(); return data.choices[0].message.content.trim(); } - + throw new Error('API响应格式不正确。'); } catch (error) { - console.error('API Call Failed:', error); + console.error('[CWB] API Call Failed:', error); throw error; - } finally { - if (console.groupEnd) { - console.groupEnd(); - } } } } diff --git a/CharacterWorldBook/src/cwb_settingsManager.js b/CharacterWorldBook/src/cwb_settingsManager.js index 199ae70..d9cee2f 100644 --- a/CharacterWorldBook/src/cwb_settingsManager.js +++ b/CharacterWorldBook/src/cwb_settingsManager.js @@ -322,8 +322,6 @@ export function bindSettingsEvents($settingsPanel) { configManager.set('cwb_api_key', apiKey); state.customApiConfig.apiKey = apiKey; updateApiStatusDisplay($panel); - - console.log('[CWB] API Key已更新 - 状态长度:', state.customApiConfig.apiKey?.length || 0); }); $panel.on('input change', '#cwb-api-model', function(event) { diff --git a/assets/rule-config-panel.html b/assets/rule-config-panel.html index f7e5d44..69ce3c0 100644 --- a/assets/rule-config-panel.html +++ b/assets/rule-config-panel.html @@ -1,14 +1,14 @@
规则配置中心 -
-
+
+
-
+
@@ -25,7 +25,7 @@
-
+
@@ -42,4 +42,39 @@ gap: 8px; align-items: center; } + #amily2_rule_config_panel_root .amily2-rule-layout { + display: flex; + gap: 16px; + align-items: flex-start; + flex-wrap: wrap; + } + #amily2_rule_config_panel_root .amily2-rule-sidebar { + width: 260px; + flex-shrink: 0; + } + #amily2_rule_config_panel_root .amily2-rule-main { + flex: 1; + min-width: 0; + } + #amily2_rule_config_panel_root .amily2-rule-actions { + display: flex; + gap: 8px; + margin-top: 16px; + flex-wrap: wrap; + } + @media (max-width: 768px) { + #amily2_rule_config_panel_root .amily2-rule-sidebar { + width: 100%; + } + #amily2_rule_config_panel_root .amily2-rule-actions > .amily2-vbtn { + flex: 1 1 calc(33.333% - 8px); + min-width: 72px; + } + #amily2_rule_config_panel_root .amily2-rule-row { + grid-template-columns: 1fr 1fr !important; + } + #amily2_rule_config_panel_root .amily2-rule-row > :last-child { + grid-column: 1 / -1; + } + } diff --git a/core/amily2-updater.js b/core/amily2-updater.js index 86d723e..cae3f84 100644 --- a/core/amily2-updater.js +++ b/core/amily2-updater.js @@ -179,9 +179,12 @@ class Amily2Updater { if (this.compareVersions(this.latestVersion, this.currentVersion) > 0) { $updateIndicator.show(); $updateButton.attr('title', `发现新版本 ${this.latestVersion}!点击查看详情`); + const safeVersion = /^[\w.+\-]{1,40}$/.test(String(this.latestVersion ?? '')) ? this.latestVersion : '未知'; $updateButtonNew .show() - .html(` 新版 ${this.latestVersion}`) + .empty() + .append($('').addClass('fas fa-gift')) + .append(document.createTextNode(` 新版 ${safeVersion}`)) .off('click') .on('click', () => this.showUpdateConfirmDialog()); } else { diff --git a/core/api.js b/core/api.js index ec142a2..9f5d5ef 100644 --- a/core/api.js +++ b/core/api.js @@ -330,10 +330,12 @@ async function fetchGoogleDirectModels(apiUrl, apiKey) { const GOOGLE_API_BASE_URL = 'https://generativelanguage.googleapis.com'; const fetchGoogleModels = async (version) => { - const url = `${GOOGLE_API_BASE_URL}/${version}/models?key=${apiKey}`; + const url = `${GOOGLE_API_BASE_URL}/${version}/models`; console.log(`[Amily2号-使节团] 正在从 Google API (${version}) 获取模型列表: ${url}`); - - const response = await fetch(url); + + const response = await fetch(url, { + headers: { 'x-goog-api-key': apiKey }, + }); if (!response.ok) { console.warn(`获取 Google API (${version}) 模型列表失败: ${response.status}`); return []; @@ -745,12 +747,13 @@ async function callGoogleDirect(messages, options) { const GOOGLE_API_BASE_URL = 'https://generativelanguage.googleapis.com'; const apiVersion = options.model.includes('gemini-1.5') ? 'v1beta' : 'v1'; - const finalApiUrl = `${GOOGLE_API_BASE_URL}/${apiVersion}/models/${options.model}:generateContent?key=${options.apiKey}`; - + const finalApiUrl = `${GOOGLE_API_BASE_URL}/${apiVersion}/models/${options.model}:generateContent`; + console.log(`[Amily2号-Google直连] API地址: ${finalApiUrl}`); - const headers = { - "Content-Type": "application/json" + const headers = { + "Content-Type": "application/json", + "x-goog-api-key": options.apiKey, }; const requestBody = JSON.stringify(convertToGoogleRequest({ diff --git a/core/auto-char-card/agent-manager.js b/core/auto-char-card/agent-manager.js index 154db48..d955a6f 100644 --- a/core/auto-char-card/agent-manager.js +++ b/core/auto-char-card/agent-manager.js @@ -485,7 +485,8 @@ Example: .replace(/<\/thinking>/gi, ''); const toolNames = Object.keys(tools); - const toolRegex = new RegExp(`<(${toolNames.join('|')})(?:\\s+[^>]*)?>[\\s\\S]*?<\\/\\1>`, 'gi'); + const escapedToolNames = toolNames.map(n => String(n).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); + const toolRegex = new RegExp(`<(${escapedToolNames.join('|')})(?:\\s+[^>]*)?>[\\s\\S]*?<\\/\\1>`, 'gi'); cleanContent = cleanContent.replace(toolRegex, '').trim(); if (cleanContent) { diff --git a/core/rag-api.js b/core/rag-api.js index b2c10b2..c6abde2 100644 --- a/core/rag-api.js +++ b/core/rag-api.js @@ -112,9 +112,11 @@ export async function fetchEmbeddingModels(overrideSettings = null) { if (!apiKey) throw new Error("Google直连模式需要API Key。"); const fetchGoogleModels = async (version) => { - const url = `${GOOGLE_API_BASE_URL}/${version}/models?key=${apiKey}`; + const url = `${GOOGLE_API_BASE_URL}/${version}/models`; console.log(`[翰林院] 正在从 Google API (${version}) 获取模型列表: ${url}`); - const response = await fetch(url); + const response = await fetch(url, { + headers: { 'x-goog-api-key': apiKey }, + }); if (!response.ok) { console.warn(`获取 Google API (${version}) 模型列表失败: ${response.status}`); return []; @@ -345,8 +347,8 @@ export async function getEmbeddings(texts, signal = null) { console.log('[翰林院-API] 使用Google直连模式获取向量。'); if (!apiKey) throw new Error('Google直连模式需要API Key。'); - // 使用适配器构建URL和请求体 - const googleUrl = `${buildGoogleEmbeddingApiUrl(GOOGLE_API_BASE_URL, embeddingModel)}?key=${apiKey}`; + // 使用适配器构建URL和请求体;Key 通过 x-goog-api-key 头传递避免 URL 泄露 + const googleUrl = buildGoogleEmbeddingApiUrl(GOOGLE_API_BASE_URL, embeddingModel); const googleBody = buildGoogleEmbeddingRequest(batch, embeddingModel); console.log(`[翰林院-API] 发送到 Google API 的请求 URL: ${googleUrl}`); @@ -356,6 +358,7 @@ export async function getEmbeddings(texts, signal = null) { method: 'POST', headers: { 'Content-Type': 'application/json', + 'x-goog-api-key': apiKey, }, body: JSON.stringify(googleBody), signal: signal, diff --git a/core/rag-processor.js b/core/rag-processor.js index a5e0684..c004a9f 100644 --- a/core/rag-processor.js +++ b/core/rag-processor.js @@ -80,12 +80,23 @@ function containsPinyinMatch(text, query) { function highlightSearchMatch(text, query) { + const safeText = String(text ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); if (!query || !query.trim()) { - return text; + return safeText; } - - const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); - return text.replace(regex, '$1'); + const safeQuery = String(query) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + const regex = new RegExp(`(${safeQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); + return safeText.replace(regex, '$1'); } function debounce(func, wait) { diff --git a/core/utils/pollingManager.js b/core/utils/pollingManager.js index 6b4f7a4..6d01aee 100644 --- a/core/utils/pollingManager.js +++ b/core/utils/pollingManager.js @@ -137,7 +137,12 @@ export function progressTracker(operationId, maxAttempts) { container.style.backgroundColor = 'rgba(80,0,0,0.9)'; progress.style.display = 'none'; info.style.whiteSpace = 'pre-wrap'; - info.innerHTML = `错误详情:\n${errorMsg}`; + info.innerHTML = ''; + const label = document.createElement('span'); + label.style.color = '#ff9494'; + label.textContent = '错误详情:'; + info.appendChild(label); + info.appendChild(document.createTextNode('\n' + String(errorMsg ?? ''))); } }; } \ No newline at end of file diff --git a/glossary/GT_bindings.js b/glossary/GT_bindings.js index 4768117..85df68e 100644 --- a/glossary/GT_bindings.js +++ b/glossary/GT_bindings.js @@ -321,7 +321,7 @@ async function renderWorldBookEntries() {
${renderContent(entry.content)}
`; @@ -377,7 +377,12 @@ async function renderWorldBookEntries() { } catch (error) { console.error('加载世界书条目失败:', error); - container.innerHTML = `

加载失败: ${error.message}

`; + const p = document.createElement('p'); + p.style.textAlign = 'center'; + p.style.color = '#ff8a8a'; + p.textContent = `加载失败: ${error?.message ?? '未知错误'}`; + container.innerHTML = ''; + container.appendChild(p); } } diff --git a/ui/api-config-bindings.js b/ui/api-config-bindings.js index d3f4701..1bad50a 100644 --- a/ui/api-config-bindings.js +++ b/ui/api-config-bindings.js @@ -522,10 +522,11 @@ async function _fetchModels($c) { let models; if (provider === 'google') { - // Google 用原生 API,以 ?key= 传参,返回 models[] 而非 data[] + // Google 用原生 API,Key 通过 x-goog-api-key 头传递避免 URL 泄露 if (!apiKey) { toastr.warning('请先填写 Google API Key。'); return; } const resp = await fetch( - `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}` + 'https://generativelanguage.googleapis.com/v1beta/models', + { headers: { 'x-goog-api-key': apiKey } } ); if (!resp.ok) { const status = resp.status; @@ -614,7 +615,8 @@ async function _testConnection($c) { return; } const resp = await fetch( - `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}` + 'https://generativelanguage.googleapis.com/v1beta/models', + { headers: { 'x-goog-api-key': apiKey } } ); if (resp.ok) { const data = await resp.json(); diff --git a/ui/hanlinyuan-bindings.js b/ui/hanlinyuan-bindings.js index 27fc36b..c89f845 100644 --- a/ui/hanlinyuan-bindings.js +++ b/ui/hanlinyuan-bindings.js @@ -33,9 +33,9 @@ function escapeAttribute(text) { } -function _populateHlyRuleProfileSelect(select, slot) { - const profiles = ruleProfileManager.listProfiles(); - const assigned = ruleProfileManager.getAssignment(slot) || ''; +function _populateHlyRuleProfileSelect(select, slot, detail) { + const profiles = detail?.profiles ?? ruleProfileManager.listProfiles(); + const assigned = detail?.assignments?.[slot] ?? ruleProfileManager.getAssignment(slot) ?? ''; select.innerHTML = [ '', ...profiles.map(p => @@ -414,9 +414,9 @@ function bindInternalUIEvents() { } // 规则配置中心保存/删除后自动刷新翰林院下拉选单 - document.addEventListener('amily2:ruleProfilesChanged', () => { - if (condensationRuleSelect) _populateHlyRuleProfileSelect(condensationRuleSelect, 'condensation'); - if (queryPrepRuleSelect) _populateHlyRuleProfileSelect(queryPrepRuleSelect, 'queryPreprocessing'); + document.addEventListener('amily2:ruleProfilesChanged', (e) => { + if (condensationRuleSelect) _populateHlyRuleProfileSelect(condensationRuleSelect, 'condensation', e.detail); + if (queryPrepRuleSelect) _populateHlyRuleProfileSelect(queryPrepRuleSelect, 'queryPreprocessing', e.detail); }); // 为自定义多选下拉框绑定事件 @@ -920,8 +920,8 @@ async function renderKnowledgeBases() { } catch (error) { console.error('[翰林院-枢纽] 渲染知识库列表失败:', error); - localContainer.innerHTML = `

加载失败: ${error.message}

`; - globalContainer.innerHTML = `

加载失败: ${error.message}

`; + localContainer.innerHTML = `

加载失败: ${escapeTextareaContent(error.message)}

`; + globalContainer.innerHTML = `

加载失败: ${escapeTextareaContent(error.message)}

`; } } @@ -1020,8 +1020,8 @@ function _createKbItemElement(id, kb, scope, vectorCount) { item.innerHTML = `
- - ${kb.name} (${vectorCount}条) + + ${escapeTextareaContent(kb.name || '')} (${Number(vectorCount) || 0}条)
${moveButtonHtml} @@ -1529,11 +1529,11 @@ function updateEntryOptions(query, allEntries) { filteredEntries.forEach(entry => { const displayText = query ? highlightSearchMatch(entry.comment, query) : - entry.comment; + escapeTextareaContent(entry.comment); const optionHtml = ` -