feat: 为项目添加 WebUI

This commit is contained in:
foxhui
2025-12-19 04:43:59 +08:00
parent 1ccaa55608
commit 41e3e022a3
33 changed files with 1842 additions and 40 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ const TARGET_URL = 'https://gemini.google.com/app?hl=en';
* @param {object} [meta={}] - 日志元数据
* @returns {Promise<{image?: string, error?: string}>}
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page } = context;
const inputLocator = page.getByRole('textbox');
const sendBtnLocator = page.getByRole('button', { name: 'Send message' });
@@ -192,5 +192,5 @@ export const manifest = {
navigationHandlers: [],
// 核心生图方法
generateImage
generate
};
+13 -2
View File
@@ -97,7 +97,7 @@ async function handleAccountChooser(page) {
* @param {string} modelId - 模型 ID (目前未使用,固定为 gemini-3-pro-preview)
* @returns {Promise<{image?: string, error?: string}>} 生成结果
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page, config } = context;
try {
@@ -266,6 +266,17 @@ export const manifest = {
id: 'gemini_biz',
displayName: 'Gemini Business',
// 配置表单定义
configSchema: [
{
key: 'entryUrl',
label: '入口 URL',
type: 'string',
required: true,
placeholder: 'https://business.gemini.google/home/cid/8888a888-b6e0-88be-86e1-888cf3ee8cf4'
}
],
// 入口 URL (从配置读取,支持新旧路径)
getTargetUrl(config, workerConfig) {
return config?.backend?.adapter?.gemini_biz?.entryUrl || config?.backend?.geminiBiz?.entryUrl || null;
@@ -286,5 +297,5 @@ export const manifest = {
navigationHandlers: [handleAccountChooser],
// 核心生图方法
generateImage
generate
};
+15 -3
View File
@@ -97,7 +97,7 @@ async function handleAccountChooser(page) {
* @param {string} modelId - 模型 ID (目前未使用,固定为 gemini-3-pro-preview)
* @returns {Promise<{image?: string, error?: string}>} 生成结果
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page, config } = context;
try {
@@ -309,7 +309,19 @@ export const manifest = {
id: 'gemini_biz_text',
displayName: 'Gemini Business (Text)',
// 入口 URL (从配置读取,支持新旧路径)
// 配置表单定义(与 gemini_biz 共享配置)
configSchema: [
{
key: 'entryUrl',
label: '入口 URL',
type: 'string',
required: true,
placeholder: 'https://business.gemini.google/home/cid/8888a888-b6e0-88be-86e1-888cf3ee8cf4',
note: '与 gemini_biz 共享配置'
}
],
// 入口 URL (从配置读取,与 gemini_biz 共享)
getTargetUrl(config, workerConfig) {
return config?.backend?.adapter?.gemini_biz?.entryUrl || config?.backend?.geminiBiz?.entryUrl || null;
},
@@ -334,5 +346,5 @@ export const manifest = {
navigationHandlers: [handleAccountChooser],
// 核心生图方法
generateImage
generate
};
+2 -2
View File
@@ -52,7 +52,7 @@ function extractImage(text) {
* @param {object} [meta={}] - 日志元数据
* @returns {Promise<{image?: string, text?: string, error?: string}>} 生成结果
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page, config } = context;
const textareaSelector = 'textarea';
@@ -222,5 +222,5 @@ export const manifest = {
navigationHandlers: [],
// 核心生图方法
generateImage
generate
};
+2 -2
View File
@@ -33,7 +33,7 @@ const TARGET_URL_SEARCH = 'https://lmarena.ai/zh/c/new?mode=direct&chat-modality
* @param {object} [meta={}] - 日志元数据
* @returns {Promise<{image?: string, text?: string, error?: string}>} 生成结果
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page, config } = context;
const textareaSelector = 'textarea';
@@ -305,5 +305,5 @@ export const manifest = {
navigationHandlers: [],
// 核心生图方法
generateImage
generate
};
+2 -2
View File
@@ -32,7 +32,7 @@ const TARGET_URL = 'https://nanobananafree.ai/';
* @param {object} [meta={}] - 日志元数据
* @returns {Promise<{image?: string, text?: string, error?: string}>} 生成结果
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page } = context;
const textareaSelector = 'textarea';
@@ -157,5 +157,5 @@ export const manifest = {
navigationHandlers: [],
// 核心生图方法
generateImage
generate
};
+2 -2
View File
@@ -40,7 +40,7 @@ async function findElementWithShadowRoot(hostHandle) {
/**
* 执行 Turnstile 验证任务
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page } = context;
try {
@@ -212,5 +212,5 @@ export const manifest = {
},
navigationHandlers: [],
generateImage
generate
};
+2 -2
View File
@@ -119,7 +119,7 @@ async function handleDiscordAuth(page) {
* @param {object} meta - 日志元数据
* @returns {Promise<{image?: string, error?: string}>} 生成结果
*/
async function generateImage(context, prompt, imgPaths, modelId, meta = {}) {
async function generate(context, prompt, imgPaths, modelId, meta = {}) {
const { page, config } = context;
try {
@@ -371,5 +371,5 @@ export const manifest = {
navigationHandlers: [handleDiscordAuth],
// 核心生图方法
generateImage
generate
};
+3 -3
View File
@@ -4,7 +4,7 @@
*
* 对外统一能力:
* - `initBrowser(cfg)` → 初始化 Pool
* - `generateImage(ctx, prompt, imagePaths, modelId, meta)`
* - `generate(ctx, prompt, imagePaths, modelId, meta)`
* - `resolveModelId(modelKey)` / `getModels()` / `getImagePolicy(modelKey)`
* - `getCookies(workerName, domain)` - 获取指定 Worker 的 Cookies
*/
@@ -67,11 +67,11 @@ export function getBackend() {
* @param {string} modelId - 模型 ID
* @param {object} meta - 元信息
*/
generateImage: async (ctx, prompt, paths, modelId, meta) => {
generate: async (ctx, prompt, paths, modelId, meta) => {
if (!poolManager) {
return { error: 'Pool 未初始化' };
}
return await poolManager.generateImage(ctx, prompt, paths, modelId, meta);
return await poolManager.generate(ctx, prompt, paths, modelId, meta);
},
/**
+2 -2
View File
@@ -156,7 +156,7 @@ export class PoolManager {
/**
* 分发生图任务(支持故障转移)
*/
async generateImage(ctx, prompt, paths, modelId, meta) {
async generate(ctx, prompt, paths, modelId, meta) {
const failoverConfig = this.config.backend?.pool?.failover || {};
const failoverEnabled = failoverConfig.enabled !== false;
const maxRetries = failoverConfig.maxRetries || 2;
@@ -213,7 +213,7 @@ export class PoolManager {
*/
async _safeExecuteWorker(worker, ctx, prompt, paths, modelId, meta) {
try {
return await worker.generateImage(ctx, prompt, paths, modelId, meta);
return await worker.generate(ctx, prompt, paths, modelId, meta);
} catch (err) {
logger.error('工作池', `[${worker.name}] 执行异常`, { error: err.message, ...meta });
return normalizeError(err.message || '执行异常');
+4 -4
View File
@@ -240,12 +240,12 @@ export class Worker {
/**
* 生成图片
*/
async generateImage(ctx, prompt, paths, modelId, meta) {
async generate(ctx, prompt, paths, modelId, meta) {
const failoverConfig = this.globalConfig.backend?.pool?.failover || {};
const failoverEnabled = failoverConfig.enabled !== false;
if (this.type === 'merge' && failoverEnabled) {
return this._generateImageWithFailover(ctx, prompt, paths, modelId, meta, failoverConfig);
return this._generateWithFailover(ctx, prompt, paths, modelId, meta, failoverConfig);
}
const resolved = this.resolveModelId(modelId);
@@ -261,7 +261,7 @@ export class Worker {
* Merge 模式下的故障转移生成
* @private
*/
async _generateImageWithFailover(ctx, prompt, paths, modelId, meta, failoverConfig = {}) {
async _generateWithFailover(ctx, prompt, paths, modelId, meta, failoverConfig = {}) {
const maxRetries = failoverConfig.maxRetries || 2;
const candidateTypes = this._getCandidateTypes(modelId);
@@ -339,7 +339,7 @@ export class Worker {
this.busyCount++;
try {
return await adapter.generateImage(subContext, prompt, paths, realId, meta);
return await adapter.generate(subContext, prompt, paths, realId, meta);
} finally {
this.busyCount--;
}
+2 -2
View File
@@ -89,8 +89,8 @@ class AdapterRegistry {
errors.push('缺少 id 或类型不正确');
}
if (!manifest.generateImage || typeof manifest.generateImage !== 'function') {
errors.push('缺少 generateImage 函数');
if (!manifest.generate || typeof manifest.generate !== 'function') {
errors.push('缺少 generate 函数');
}
if (!manifest.models || !Array.isArray(manifest.models)) {