Files
ST-Amily2-Chat-Optimisation/SL/bus/Amily2Bus.js

180 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Logger from './log/Logger.js';
import FilePipe from './file/FilePipe.js';
import ModelCaller from './api/ModelCaller.js';
import Options from './api/Options.js';
// 【逃生通道】创建一个纯净的 Console 对象,绕过任何潜在的劫持
const getSafeConsole = () => {
try {
if (window._amilySafeConsole) return window._amilySafeConsole;
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
const safe = iframe.contentWindow.console;
// document.body.removeChild(iframe); // 保持 iframe 以维持 console 引用有效
window._amilySafeConsole = safe;
return safe;
} catch (e) {
return window.console; // Fallback
}
};
class Amily2Bus {
constructor() {
this.safeConsole = getSafeConsole();
// 1. 初始化 Logger
/** @type {Logger} */
this.Logger = new Logger(this.safeConsole);
/** @type {FilePipe} */
this.FilePipe = new FilePipe();
// 2. 依赖注入 (Dependency Injection)
// 创建一个 Logger 代理适配器传给 ModelCaller
const loggerDelegate = {
log: (type, message, origin, plugin) => {
// 回调 Bus 的 Logger 实例
this.Logger.process(plugin || 'Global', origin || 'Model', type, message);
}
};
// ModelCaller 不再包含 Bus只包含 logger 代理
/** @type {ModelCaller} */
this.ModelCaller = new ModelCaller(loggerDelegate);
// 存储上下文引用(严格锁:每个插件名仅限一次成功注册)
this.registry = new Map();
// 存储公开的联动接口(联动模块)
this.publicRegistry = new Map();
this.safeConsole.log('[Amily2Bus] Core Initialized (Decoupled Architecture).');
// 3. 自动注册并锁定 PUBLIC 命名空间
this._initPublicNamespace();
}
/**
* 初始化系统保留的 PUBLIC 模块
* 用于提供系统级信息的联动查询,防止 PUBLIC 命名被滥用
*/
_initPublicNamespace() {
try {
// 这里利用 register 的机制,直接抢占 'PUBLIC' 并加上严格锁
const sysCtx = this.register('PUBLIC');
// 暴露系统级能力给 query('PUBLIC')
sysCtx.expose({
description: 'Amily2 System Public Interface',
version: '2.0.0-Core',
// 允许查询当前有哪些插件暴露了公共接口
getAvailableModules: () => {
return Array.from(this.publicRegistry.keys());
},
// 允许查询当前所有已注册(被锁定的)插件名
getRegisteredPlugins: () => {
return Array.from(this.registry.keys());
},
// 简单的系统状态检查
ping: () => 'pong'
});
// 内部记录一条初始化日志
sysCtx.log('System', 'info', 'PUBLIC namespace reserved and strictly locked.');
} catch (e) {
this.safeConsole.error('[Amily2Bus] CRITICAL: Failed to init PUBLIC namespace.', e);
}
}
/**
* 直接记录系统级日志 (Global Scope)
* 支持手动指定来源,方便终端调试或非插件环境调用
* @param {string} type 日志级别 (debug, info, warn, error)
* @param {string} message 消息内容
* @param {string} [origin='Bus' 来源模块,默认为 'Bus'
* @param {string} [plugin='Global'] 来源插件/命名空间,调试时可指定如 'Console'
*/
log(type, message, origin = 'Bus', plugin = 'Global') {
if (this.Logger) {
this.Logger.process(plugin, origin, type, message);
}
}
/**
* 注册插件并获取专属上下文 (严格锁机制)
* @param {string} pluginName 插件名称
* @returns {Object} 包含该插件专属 API 的上下文对象
*/
register(pluginName) {
if (!pluginName || typeof pluginName !== 'string') {
throw new Error('[Amily2Bus] Invalid plugin name.');
}
if (this.registry.has(pluginName)) {
const errorMsg = `[Amily2Bus] Security Error: Plugin '${pluginName}' is already registered and locked.`;
this.safeConsole.error(errorMsg);
throw new Error(errorMsg);
}
// 返回该插件专属的 API 上下文 (Capability Token)
const context = {
// 1. 日志能力 (绑定了身份的日志接口)
log: (origin, type, message) => this.Logger.log(pluginName, origin, type, message),
// 2. 文件能力 (绑定了身份的文件接口)
file: {
read: (path) => {
return this.FilePipe ? this.FilePipe.read(pluginName, path) : null;
},
write: (path, data) => {
return this.FilePipe ? this.FilePipe.write(pluginName, path, data) : false;
}
},
// 3. 网络能力 (ModelCaller)
model: {
// 暴露 Options 类,方便插件直接 new context.model.Options() 或使用 builder
Options: Options,
// 插件调用时Bus 负责将 pluginName 传给无状态的 ModelCaller
call: (messages, options) => this.ModelCaller.call(pluginName, messages, options)
},
/**
* 将本插件的能力暴露给公共查询池,实现插件间联动
* @param {Object} apiMethods
*/
expose: (apiMethods) => {
if (typeof apiMethods !== 'object') throw new Error('Exposed API must be an object');
this.publicRegistry.set(pluginName, Object.freeze(apiMethods));
this.log('info', `Module exposed to public registry.`, 'Bus', pluginName);
}
};
this.registry.set(pluginName, context);
this.safeConsole.log(`[Amily2Bus] Plugin registered: ${pluginName}`);
return context;
}
/**
* 联动查询:获取其他插件通过 expose 暴露的能力
* @param {string} pluginName 目标插件名称
* @returns {Object|null}
*/
query(pluginName) {
return this.publicRegistry.get(pluginName) || null;
}
}
// 挂载全局单例
if (!window.Amily2Bus) {
window.Amily2Bus = new Amily2Bus();
}
export function initializeAmilyBus() {
if (!(window.Amily2Bus instanceof Amily2Bus)) {
window.Amily2Bus = new Amily2Bus();
console.log('[Amily2] Amily2Bus 已成功初始化并附加到 window 对象');
}
}