mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 06:55:51 +00:00
153 lines
4.8 KiB
JavaScript
153 lines
4.8 KiB
JavaScript
import { eventSource, event_types } from '/script.js';
|
|
|
|
export const LOG_LEVELS = {
|
|
DEBUG: 0b0001,
|
|
INFO: 0b0010,
|
|
WARN: 0b0100,
|
|
ERROR: 0b1000,
|
|
NONE: 0
|
|
};
|
|
|
|
class FilePipe {
|
|
constructor(pluginName, bus) {
|
|
this.pluginName = pluginName;
|
|
this.bus = bus;
|
|
this.basePath = `extensions/${pluginName}`;
|
|
}
|
|
|
|
/**
|
|
* 验证路径是否合法 (仅允许访问插件自己的目录)
|
|
* @param {string} path
|
|
* @returns {boolean}
|
|
*/
|
|
validatePath(path) {
|
|
// 简单示例:防止 ../ 越权,并确保路径以 basePath 或允许的相对路径开头
|
|
// 实际上 SillyTavern 的 writeFile API 通常已经限制在特定目录,
|
|
// 但这里我们做逻辑层的隔离。
|
|
if (path.includes('..')) {
|
|
this.bus.log(this.pluginName, 'FilePipe', 'ERROR', `非法路径尝试: ${path}`);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async read(fileName) {
|
|
if (!this.validatePath(fileName)) throw new Error("Access Denied");
|
|
// 这里对接实际的文件读取逻辑,例如 fetch 或 ST 的 API
|
|
// return await readFileContent(this.basePath + '/' + fileName);
|
|
console.log(`[Mock] Reading ${fileName} for ${this.pluginName}`);
|
|
}
|
|
|
|
async write(fileName, content) {
|
|
if (!this.validatePath(fileName)) throw new Error("Access Denied");
|
|
// 这里对接实际的文件写入逻辑
|
|
// return await writeFileContent(this.basePath + '/' + fileName, content);
|
|
console.log(`[Mock] Writing to ${fileName} for ${this.pluginName}`);
|
|
}
|
|
}
|
|
|
|
class Amily2Bus {
|
|
constructor() {
|
|
this.plugins = new Map();
|
|
this.logConfig = {
|
|
globalLevel: LOG_LEVELS.INFO,
|
|
filters: {} // e.g., "Amily2.Network": LOG_LEVELS.DEBUG
|
|
};
|
|
console.log("[Amily2-Bus] 总线系统初始化...");
|
|
}
|
|
|
|
/**
|
|
* 注册插件
|
|
* @param {Object} manifest - 必须包含 name 字段
|
|
* @param {Object} instance - 插件实例或主要对象
|
|
* @returns {FilePipe|null} - 返回专属的文件管线对象,注册失败返回 null
|
|
*/
|
|
register(manifest, instance = {}) {
|
|
if (!manifest || !manifest.name) {
|
|
this.log('System', 'Bus', 'ERROR', '注册失败: 无效的 manifest');
|
|
return null;
|
|
}
|
|
|
|
const name = manifest.name;
|
|
if (this.plugins.has(name)) {
|
|
this.log('System', 'Bus', 'WARN', `插件 ${name} 已注册,正在覆盖...`);
|
|
}
|
|
|
|
this.plugins.set(name, {
|
|
manifest,
|
|
instance,
|
|
registeredAt: Date.now()
|
|
});
|
|
|
|
this.log('System', 'Bus', 'INFO', `插件注册成功: ${name}`);
|
|
|
|
// 返回该插件专属的 FilePipe
|
|
return new FilePipe(name, this);
|
|
}
|
|
|
|
/**
|
|
* 获取已注册的插件实例
|
|
* @param {string} name
|
|
*/
|
|
getPlugin(name) {
|
|
return this.plugins.get(name)?.instance;
|
|
}
|
|
|
|
/**
|
|
* 设置日志级别
|
|
* @param {string} target - "Global" 或 "PluginName.ModuleName"
|
|
* @param {string|number} level - "DEBUG", "INFO", etc.
|
|
*/
|
|
setLogLevel(target, level) {
|
|
const lvl = typeof level === 'string' ? LOG_LEVELS[level.toUpperCase()] : level;
|
|
if (target === 'Global') {
|
|
this.logConfig.globalLevel = lvl;
|
|
} else {
|
|
this.logConfig.filters[target] = lvl;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 统一日志方法
|
|
* @param {string} plugin - 插件名
|
|
* @param {string} module - 模块名
|
|
* @param {string} level - 级别 (DEBUG, INFO, WARN, ERROR)
|
|
* @param {string|Object} message - 内容
|
|
*/
|
|
log(plugin, module, level, message) {
|
|
const currentLevel = LOG_LEVELS[level.toUpperCase()] || LOG_LEVELS.INFO;
|
|
const configKey = `${plugin}.${module}`;
|
|
|
|
// 检查特定配置,如果没有则使用全局配置
|
|
const requiredLevel = this.logConfig.filters[configKey] !== undefined
|
|
? this.logConfig.filters[configKey]
|
|
: this.logConfig.globalLevel;
|
|
|
|
if (currentLevel < requiredLevel) {
|
|
return; // 级别不足,忽略
|
|
}
|
|
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
const formattedMsg = typeof message === 'object' ? JSON.stringify(message) : message;
|
|
|
|
// 样式化输出
|
|
let color = '#7f8c8d'; // Default (INFO)
|
|
if (level === 'WARN') color = '#f39c12';
|
|
if (level === 'ERROR') color = '#c0392b';
|
|
if (level === 'DEBUG') color = '#3498db';
|
|
|
|
console.log(
|
|
`%c[${timestamp}] [${plugin}::${module}]%c ${formattedMsg}`,
|
|
`color: ${color}; font-weight: bold;`,
|
|
`color: inherit;`
|
|
);
|
|
}
|
|
}
|
|
|
|
// 单例模式,挂载到 window 方便全局调用
|
|
if (!window.Amily2Bus) {
|
|
window.Amily2Bus = new Amily2Bus();
|
|
}
|
|
|
|
export default window.Amily2Bus;
|