mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-07 06:15:51 +00:00
feat: add API config system, FilePipe backend, and ConfigManager
- ConfigManager: route sensitive keys (API keys) to localStorage, migrate existing values out of extension_settings on startup - ApiKeyStore: local/cloud storage modes with RSA+AES hybrid encryption - ApiProfileManager: named connection profiles (chat/embedding/rerank) with per-slot type-validated assignments - FilePipe: complete IndexedDB backend (read/write/delete/list/stat) - Amily2Bus: inject FilePipe via forPlugin() capability token - UI: api-config-panel with profile CRUD and slot assignment - TableSystemService: initial service layer scaffold - logger.js: XSS fix
This commit is contained in:
@@ -1,75 +1,19 @@
|
||||
import { getContext, extension_settings } from "/scripts/extensions.js";
|
||||
import { saveChatConditional } from "/script.js";
|
||||
import { extensionName } from "../utils/settings.js";
|
||||
import * as TableManager from './table-system/manager.js';
|
||||
import * as Executor from './table-system/executor.js';
|
||||
import { renderTables } from '../ui/table-bindings.js';
|
||||
import { log } from "./table-system/logger.js";
|
||||
|
||||
async function handleTableUpdate(messageId) {
|
||||
TableManager.clearHighlights();
|
||||
|
||||
const settings = extension_settings[extensionName];
|
||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||
if (!tableSystemEnabled) {
|
||||
log('【监察系统】表格系统总开关已关闭,跳过所有表格处理。', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
const fillingMode = settings.filling_mode || 'main-api';
|
||||
if (fillingMode === 'secondary-api' || fillingMode === 'optimized') {
|
||||
log('【监察系统】检测到"分步填表"或"优化中填表"模式已启用,主API填表逻辑已自动禁用。', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`【监察系统】接到圣旨,开始处理消息 ID: ${messageId}`, 'warn');
|
||||
const context = getContext();
|
||||
const message = context.chat[messageId];
|
||||
|
||||
if (!message) {
|
||||
log(`【监察系统】错误:未找到消息 ID: ${messageId},流程中止。`, 'error');
|
||||
return;
|
||||
}
|
||||
if (message.is_user) {
|
||||
log(`【监察系统】消息 ID: ${messageId} 是用户消息,无需处理。`, 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`【监察系统】正在处理的奏折内容: "${message.mes.substring(0, 50)}..."`, 'info');
|
||||
const initialState = TableManager.loadTables(messageId);
|
||||
log(`【监察系统-步骤1】为消息 ${messageId} 加载了基准状态。`, 'info', initialState);
|
||||
const { finalState, hasChanges, changes } = Executor.executeCommands(message.mes, initialState);
|
||||
log(`【监察系统-步骤2】推演完毕。是否有变化: ${hasChanges}`, 'info', finalState);
|
||||
if (hasChanges) {
|
||||
if (changes && changes.length > 0) {
|
||||
changes.forEach(change => {
|
||||
TableManager.addHighlight(change.tableIndex, change.rowIndex, change.colIndex);
|
||||
});
|
||||
}
|
||||
|
||||
TableManager.saveStateToMessage(finalState, message);
|
||||
TableManager.setMemoryState(finalState);
|
||||
await saveChatConditional();
|
||||
log(`【监察系统-步骤3】检测到变化,已将新状态写入消息 ${messageId} 并保存。`, 'success');
|
||||
} else {
|
||||
log(`【监察系统-步骤3】未检测到有效指令或变化,无需写入。`, 'info');
|
||||
}
|
||||
if (hasChanges) {
|
||||
renderTables();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import { processMessageUpdate, fillWithSecondaryApi } from './table-system/TableSystemService.js';
|
||||
|
||||
import { processOptimization } from "./summarizer.js";
|
||||
import { executeAutoHide } from './autoHideManager.js';
|
||||
import { checkAndTriggerAutoSummary } from './historiographer.js';
|
||||
import { fillWithSecondaryApi } from './table-system/secondary-filler.js';
|
||||
import { amilyHelper } from './tavern-helper/main.js';
|
||||
|
||||
async function handleTableUpdate(messageId) {
|
||||
await processMessageUpdate(messageId);
|
||||
}
|
||||
|
||||
export async function onMessageReceived(data) {
|
||||
window.lastPreOptimizationResult = null;
|
||||
document.dispatchEvent(new CustomEvent('preOptimizationTextUpdated'));
|
||||
document.dispatchEvent(new CustomEvent('preOptimizationTextUpdated'));
|
||||
|
||||
const context = getContext();
|
||||
if ((data && data.is_user) || context.isWaitingForUserInput) { return; }
|
||||
@@ -81,9 +25,10 @@ export async function onMessageReceived(data) {
|
||||
const latestMessage = chat[chat.length - 1];
|
||||
if (latestMessage.is_user) { return; }
|
||||
|
||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||
|
||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||
|
||||
await executeAutoHide();
|
||||
|
||||
const isOptimizationEnabled = settings.optimizationEnabled && settings.apiUrl;
|
||||
if (isOptimizationEnabled) {
|
||||
if (chat.length >= 2 && chat[chat.length - 2].is_user) {
|
||||
@@ -109,13 +54,14 @@ export async function onMessageReceived(data) {
|
||||
console.log("[Amily2号-正文优化] 检测到消息并非AI对用户的直接回复,已跳过优化。");
|
||||
}
|
||||
}
|
||||
|
||||
if (tableSystemEnabled) {
|
||||
const fillingMode = settings.filling_mode || 'main-api';
|
||||
if (fillingMode === 'secondary-api') {
|
||||
fillWithSecondaryApi(latestMessage);
|
||||
}
|
||||
} else {
|
||||
log('[分步填表] 表格系统总开关已关闭,跳过分步填表处理。', 'info');
|
||||
console.log('[分步填表] 表格系统总开关已关闭,跳过分步填表处理。');
|
||||
}
|
||||
|
||||
(async () => {
|
||||
|
||||
122
core/table-system/TableSystemService.js
Normal file
122
core/table-system/TableSystemService.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* TableSystemService
|
||||
* 表格系统 Bus 服务 — 统一对外入口
|
||||
*
|
||||
* 职责:
|
||||
* 1. 将原 events.js::handleTableUpdate 的消息处理编排逻辑收归此处
|
||||
* 2. 通过 Amily2Bus 暴露稳定接口,解耦外部模块的直接依赖
|
||||
* 3. 向后兼容:保留具名导出,现有直接 import 无需立即修改
|
||||
*
|
||||
* Bus 注册名:'TableSystem'
|
||||
*
|
||||
* 公开接口(query('TableSystem')):
|
||||
* processMessageUpdate(messageId) — 处理 AI 消息的表格更新流程
|
||||
* fillWithSecondaryApi(msg) — 二次 API 填表
|
||||
* injectTableData(...) — 向提示词注入表格数据
|
||||
* generateTableContent() — 生成表格注入内容字符串
|
||||
* getMemoryState() — 读取当前表格内存状态
|
||||
* renderTables() — 强制重渲染表格 UI
|
||||
*/
|
||||
|
||||
import { getContext, extension_settings } from "/scripts/extensions.js";
|
||||
import { saveChatConditional } from "/script.js";
|
||||
import { extensionName } from "../../utils/settings.js";
|
||||
|
||||
// ── table-system 内部模块 ─────────────────────────────────────────────────
|
||||
// manager.js / logger.js 为受限文件(不修改),此处仅引用其导出
|
||||
import * as TableManager from './manager.js';
|
||||
import { executeCommands } from './executor.js';
|
||||
import { log } from './logger.js';
|
||||
|
||||
// 可修改子模块
|
||||
import { generateTableContent, injectTableData } from './injector.js';
|
||||
import { fillWithSecondaryApi } from './secondary-filler.js';
|
||||
|
||||
// UI 层
|
||||
import { renderTables } from '../../ui/table-bindings.js';
|
||||
|
||||
// ── 核心逻辑 ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 处理单条 AI 消息的表格更新流程。
|
||||
* 原 events.js::handleTableUpdate 的完整逻辑迁移至此。
|
||||
*
|
||||
* @param {number} messageId - 消息在 context.chat 中的索引
|
||||
*/
|
||||
async function processMessageUpdate(messageId) {
|
||||
TableManager.clearHighlights();
|
||||
|
||||
const settings = extension_settings[extensionName];
|
||||
const tableSystemEnabled = settings.table_system_enabled !== false;
|
||||
if (!tableSystemEnabled) {
|
||||
log('【表格服务】表格系统总开关已关闭,跳过所有表格处理。', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
const fillingMode = settings.filling_mode || 'main-api';
|
||||
if (fillingMode === 'secondary-api' || fillingMode === 'optimized') {
|
||||
log('【表格服务】检测到"分步填表"或"优化中填表"模式,主API填表已自动禁用。', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`【表格服务】开始处理消息 ID: ${messageId}`, 'warn');
|
||||
const context = getContext();
|
||||
const message = context.chat[messageId];
|
||||
|
||||
if (!message) {
|
||||
log(`【表格服务】错误:未找到消息 ID: ${messageId},流程中止。`, 'error');
|
||||
return;
|
||||
}
|
||||
if (message.is_user) {
|
||||
log(`【表格服务】消息 ID: ${messageId} 是用户消息,跳过。`, 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`【表格服务】处理内容: "${message.mes.substring(0, 50)}..."`, 'info');
|
||||
const initialState = TableManager.loadTables(messageId);
|
||||
log('【表格服务-步骤1】基准状态已加载。', 'info', initialState);
|
||||
|
||||
const { finalState, hasChanges, changes } = executeCommands(message.mes, initialState);
|
||||
log(`【表格服务-步骤2】推演完毕。是否有变化: ${hasChanges}`, 'info', finalState);
|
||||
|
||||
if (hasChanges) {
|
||||
changes.forEach(change => {
|
||||
TableManager.addHighlight(change.tableIndex, change.rowIndex, change.colIndex);
|
||||
});
|
||||
TableManager.saveStateToMessage(finalState, message);
|
||||
TableManager.setMemoryState(finalState);
|
||||
await saveChatConditional();
|
||||
log('【表格服务-步骤3】状态已写入并保存。', 'success');
|
||||
renderTables();
|
||||
} else {
|
||||
log('【表格服务-步骤3】未检测到有效指令或变化,无需写入。', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bus 注册 ──────────────────────────────────────────────────────────────
|
||||
// 使用 setTimeout 延迟到同步模块初始化完成后再注册,
|
||||
// 确保 window.Amily2Bus 已由 SL/bus/Amily2Bus.js 完成挂载。
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const _ctx = window.Amily2Bus?.register('TableSystem');
|
||||
if (!_ctx) {
|
||||
console.warn('[TableSystem] Amily2Bus 尚未就绪,服务注册跳过。');
|
||||
return;
|
||||
}
|
||||
_ctx.expose({
|
||||
processMessageUpdate,
|
||||
fillWithSecondaryApi,
|
||||
injectTableData,
|
||||
generateTableContent,
|
||||
getMemoryState: () => TableManager.getMemoryState(),
|
||||
renderTables,
|
||||
});
|
||||
_ctx.log('TableSystemService', 'info', 'TableSystem 服务已注册到 Bus。');
|
||||
} catch (e) {
|
||||
console.error('[TableSystem] Bus 注册失败:', e);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// ── 向后兼容具名导出 ──────────────────────────────────────────────────────
|
||||
// 过渡期保留,现有 import { ... } from '...TableSystemService.js' 无需修改。
|
||||
export { processMessageUpdate, fillWithSecondaryApi, generateTableContent, injectTableData };
|
||||
@@ -1 +1,30 @@
|
||||
const _0x352fc5=_0xb01f;(function(_0x52276c,_0x1fe640){const _0x25137c=_0xb01f,_0x322b57=_0x52276c();while(!![]){try{const _0xb9a91d=parseInt(_0x25137c(0x1d4))/0x1+-parseInt(_0x25137c(0x1d9))/0x2*(parseInt(_0x25137c(0x1c6))/0x3)+parseInt(_0x25137c(0x1c8))/0x4*(-parseInt(_0x25137c(0x1da))/0x5)+-parseInt(_0x25137c(0x1d5))/0x6+-parseInt(_0x25137c(0x1c5))/0x7+parseInt(_0x25137c(0x1c4))/0x8*(-parseInt(_0x25137c(0x1cc))/0x9)+parseInt(_0x25137c(0x1ce))/0xa;if(_0xb9a91d===_0x1fe640)break;else _0x322b57['push'](_0x322b57['shift']());}catch(_0x57e5d4){_0x322b57['push'](_0x322b57['shift']());}}}(_0x13eb,0x61073));function _0xb01f(_0x2b709c,_0x43aa7d){const _0x13eb95=_0x13eb();return _0xb01f=function(_0xb01f68,_0x3325be){_0xb01f68=_0xb01f68-0x1c4;let _0x638bf1=_0x13eb95[_0xb01f68];return _0x638bf1;},_0xb01f(_0x2b709c,_0x43aa7d);}function _0x13eb(){const _0x3421fa=['createElement','\x22></i>\x20','101174LOTkJv','79935LtdznB','3176dnnOAA','861679gOvdAF','33vyMqZa','fa-solid\x20fa-check-circle','28LBJaGM','scrollHeight','fa-solid\x20fa-circle-info','getElementById','7677IWXntE','[内存储司-起居注]\x20','13572940eKjSAe','fa-solid\x20fa-circle-xmark','appendChild','log','hly-log-entry\x20log-','innerHTML','182086ttYsxR','71094AjxVJw','fa-solid\x20fa-triangle-exclamation'];_0x13eb=function(){return _0x3421fa;};return _0x13eb();}const getLogContainer=()=>document[_0x352fc5(0x1cb)]('table-log-display');export function log(_0x1e7922,_0x4de68c='info',_0x2aabe2=null){const _0x17dbe1=_0x352fc5,_0x4fdf31=getLogContainer();if(!_0x4fdf31){const _0xec84ec=console[_0x4de68c]||console[_0x17dbe1(0x1d1)];_0xec84ec(_0x17dbe1(0x1cd)+_0x1e7922,_0x2aabe2||'');return;}const _0x483576={'info':_0x17dbe1(0x1ca),'success':_0x17dbe1(0x1c7),'warn':_0x17dbe1(0x1d6),'error':_0x17dbe1(0x1cf)},_0x5bed08=document[_0x17dbe1(0x1d7)]('p');_0x5bed08['className']=_0x17dbe1(0x1d2)+_0x4de68c,_0x5bed08[_0x17dbe1(0x1d3)]='<i\x20class=\x22'+_0x483576[_0x4de68c]+_0x17dbe1(0x1d8)+_0x1e7922,_0x4fdf31[_0x17dbe1(0x1d0)](_0x5bed08),_0x4fdf31['scrollTop']=_0x4fdf31[_0x17dbe1(0x1c9)];}
|
||||
const getLogContainer = () => document.getElementById('table-log-display');
|
||||
|
||||
export function log(message, type = 'info', data = null) {
|
||||
const container = getLogContainer();
|
||||
if (!container) {
|
||||
// 在容器不可用时,静默地将日志打印到控制台,不再显示警告
|
||||
const logFunc = console[type] || console.log;
|
||||
logFunc(`[内存储司-起居注] ${message}`, data || '');
|
||||
return;
|
||||
}
|
||||
|
||||
const iconMap = {
|
||||
info: 'fa-solid fa-circle-info',
|
||||
success: 'fa-solid fa-check-circle',
|
||||
warn: 'fa-solid fa-triangle-exclamation',
|
||||
error: 'fa-solid fa-circle-xmark',
|
||||
};
|
||||
|
||||
const logEntry = document.createElement('p');
|
||||
logEntry.className = `hly-log-entry log-${type}`;
|
||||
const icon = document.createElement('i');
|
||||
icon.className = iconMap[type];
|
||||
logEntry.appendChild(icon);
|
||||
logEntry.appendChild(document.createTextNode(` ${message}`));
|
||||
|
||||
container.appendChild(logEntry);
|
||||
|
||||
// Auto-scroll to the bottom
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user