ci: auto build & obfuscate [2026-05-16 19:16:28] (Jenkins #21)

This commit is contained in:
Jenkins CI
2026-05-16 19:16:28 +08:00
parent 4bc6e0a047
commit d9fa3072a2
46 changed files with 4154 additions and 1584 deletions

157
utils/api-vendor.js Normal file
View File

@@ -0,0 +1,157 @@
/**
* @file API 厂商识别 + 参数 registry 查询。
*
* Registry 文件assets/api-vendor-params.json
* 加载策略:模块首次调用时 fetch 一次,缓存到 _registry。
*
* 提供的能力:
* - detectVendor(apiUrl) → vendorId | null
* - getVendorEntry(vendorId) → 完整 vendor 对象(含 params 元信息)
* - listVendorParams(vendorId) → [{ name, type, desc, ... }]
* - getRegistry() → 整个 registrydebug 用)
*
* Phase A 仅用于 customParams 编辑器的提示展示,不做强制校验。
* Phase B 计划:迁移现有 15 处散乱的 `apiUrl.includes('googleapis.com')` 等检查到 detectVendor 单一入口。
*/
import { extensionName } from './settings.js';
const REGISTRY_PATH = `scripts/extensions/third-party/${extensionName}/assets/api-vendor-params.json`;
/** @type {Promise<any> | null} */
let _registryPromise = null;
/** @type {any | null} */
let _registry = null;
/**
* 懒加载 registry缓存到模块作用域。多次调用只 fetch 一次。
* @returns {Promise<any>}
*/
async function _loadRegistry() {
if (_registry) return _registry;
if (!_registryPromise) {
_registryPromise = fetch(REGISTRY_PATH)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status} 加载 ${REGISTRY_PATH} 失败`);
return res.json();
})
.then(data => {
_registry = data;
return data;
})
.catch(err => {
console.error('[api-vendor] registry 加载失败:', err);
// 降级到内置最小 fallback保证业务不中断
_registry = {
version: 0,
vendors: [],
fallback: { id: 'openai-compat', displayName: 'OpenAI-compatible', params: {} },
};
return _registry;
});
}
return _registryPromise;
}
/**
* 强制刷新 registry开发期热更新或测试用
*/
export async function reloadRegistry() {
_registry = null;
_registryPromise = null;
return _loadRegistry();
}
/**
* 返回完整 registry。供 UI 列举所有 vendor、debug 用。
* @returns {Promise<any>}
*/
export async function getRegistry() {
return _loadRegistry();
}
/**
* 根据 apiUrl 识别 vendor。匹配不上时返回 fallback.id默认 'openai-compat')。
* 大小写不敏感的 substring 匹配。
*
* @param {string} apiUrl
* @returns {Promise<string | null>}
*/
export async function detectVendor(apiUrl) {
const reg = await _loadRegistry();
const url = (apiUrl || '').toLowerCase();
if (!url) return reg.fallback?.id || null;
for (const vendor of reg.vendors || []) {
const matches = vendor.match || [];
if (matches.some(m => url.includes(String(m).toLowerCase()))) {
return vendor.id;
}
}
return reg.fallback?.id || null;
}
/**
* 根据 vendorId 取完整 vendor 对象(含 params 元信息)。
* fallback id 也能查到。
*
* @param {string | null | undefined} vendorId
* @returns {Promise<any | null>}
*/
export async function getVendorEntry(vendorId) {
if (!vendorId) return null;
const reg = await _loadRegistry();
if (reg.fallback && vendorId === reg.fallback.id) return reg.fallback;
return (reg.vendors || []).find(v => v.id === vendorId) || null;
}
/**
* 列出指定 vendor 的所有标准参数(已过滤掉 _doc / _warning_* 这类 meta 字段)。
*
* @param {string | null | undefined} vendorId
* @returns {Promise<Array<{ name: string, type?: string, range?: number[], values?: string[], desc?: string }>>}
*/
export async function listVendorParams(vendorId) {
const entry = await getVendorEntry(vendorId);
if (!entry || !entry.params) return [];
return Object.entries(entry.params)
.filter(([k]) => !k.startsWith('_'))
.map(([name, meta]) => ({ name, ...(meta || {}) }));
}
/**
* 同步版:从已加载的 registry 直接查询。仅在确知已 await 过 _loadRegistry 后使用,
* 主要给 UI render 循环用(避免 React-style 异步重渲染)。
* registry 未加载时返回空。
*
* @param {string} apiUrl
* @returns {string | null}
*/
export function detectVendorSync(apiUrl) {
if (!_registry) return null;
const url = (apiUrl || '').toLowerCase();
if (!url) return _registry.fallback?.id || null;
for (const vendor of _registry.vendors || []) {
const matches = vendor.match || [];
if (matches.some(m => url.includes(String(m).toLowerCase()))) {
return vendor.id;
}
}
return _registry.fallback?.id || null;
}
/**
* 同步版 listVendorParams。同样要求 registry 已 preload。
* @param {string | null | undefined} vendorId
* @returns {Array<{ name: string, type?: string, range?: number[], values?: string[], desc?: string }>}
*/
export function listVendorParamsSync(vendorId) {
if (!_registry || !vendorId) return [];
let entry = null;
if (_registry.fallback && vendorId === _registry.fallback.id) entry = _registry.fallback;
else entry = (_registry.vendors || []).find(v => v.id === vendorId) || null;
if (!entry || !entry.params) return [];
return Object.entries(entry.params)
.filter(([k]) => !k.startsWith('_'))
.map(([name, meta]) => ({ name, ...(meta || {}) }));
}

File diff suppressed because one or more lines are too long

View File

@@ -35,6 +35,7 @@ import { extension_settings } from "/scripts/extensions.js";
import { saveSettingsDebounced } from "/script.js";
import { extensionName } from "../settings.js";
import { apiKeyStore } from "./api-key-store/ApiKeyStore.js";
import { configManager } from "./ConfigManager.js";
// ── 类型与功能槽定义 ──────────────────────────────────────────────────────────
@@ -71,6 +72,7 @@ export const SLOTS = {
cwb: { label: '角色世界书', type: 'chat' },
autoCharCard: { label: '一键生卡', type: 'chat' },
sybd: { label: '术语表填写', type: 'chat' },
tableFilling: { label: '表格填表 / 重整', type: 'chat' },
// Embedding 槽
ragEmbed: { label: 'RAG 向量化', type: 'embedding' },
// Rerank 槽
@@ -252,6 +254,11 @@ class ApiProfileManager {
...base,
maxTokens: data.maxTokens ?? 65500,
temperature: data.temperature ?? 1.0,
// 自定义参数:透传到 LLM 请求 body 的额外 key/valuetop_p、frequency_penalty 等)
// 由 utils/api-vendor.js 提供 vendor 标准参数提示,但不强校验。
customParams: (typeof data.customParams === 'object' && data.customParams !== null)
? data.customParams
: {},
};
}
if (type === 'embedding') {
@@ -295,6 +302,278 @@ export const apiProfileManager = new ApiProfileManager();
}
})();
// ── Profile.provider 迁移 ────────────────────────────────────────────────────
// Phase B 改造:旧 'openai' 是"OpenAI 兼容总称",现在拆为 6 个具体 vendor + 'custom_oai'。
// 按 URL substring 推断真实 vendor推断不出来 → 改成 'custom_oai'URL 为空 → 保持 'openai'。
// 仅迁移 provider==='openai' 的旧 profile新值anthropic/openrouter/deepseek/xai/custom_oai/google/...)一概不动。
function _detectVendorFromUrlSync(url) {
if (!url) return null;
const lower = String(url).toLowerCase();
if (lower.includes('anthropic.com')) return 'anthropic';
if (lower.includes('openrouter.ai')) return 'openrouter';
if (lower.includes('googleapis.com') || lower.includes('aistudio.google.com')) return 'google';
if (lower.includes('deepseek.com')) return 'deepseek';
if (lower.includes('x.ai') || lower.includes('xai.com')) return 'xai';
if (lower.includes('openai.com')) return 'openai';
return null;
}
;(() => {
try {
const s = extension_settings[extensionName];
if (!s || !Array.isArray(s[EXT_PROFILES])) return;
let migratedCount = 0;
for (const profile of s[EXT_PROFILES]) {
if (profile?.provider !== 'openai') continue; // 已是新值或非 chat profile
const detected = _detectVendorFromUrlSync(profile.apiUrl);
if (detected && detected !== 'openai') {
profile.provider = detected;
migratedCount++;
} else if (profile.apiUrl && !detected) {
// URL 填了但不匹配任何已知厂商 → 标记为 custom_oai
profile.provider = 'custom_oai';
migratedCount++;
}
// URL 为空(新建中)或确实是 openai.com → 保持 'openai'
}
if (migratedCount > 0) {
console.info(`[ApiProfiles] 迁移: ${migratedCount} 个 profile 的 provider 字段已按 URL 重分类。`);
saveSettingsDebounced();
}
} catch (e) {
console.warn('[ApiProfiles] provider 迁移失败:', e);
}
})();
// ── Legacy → Profile 自动迁移v2.1.x─────────────────────────────────────
// 对每个 chat slot若没分配 profile 且旧字段apiUrl + model 都填了)存在,
// 自动建一个 profile + 迁移 API Key + 分配给该 slot。
// 幂等:通过 _legacyProfileMigrationDone 标记,只在首次 ship 后跑一次。
// 旧字段保留不动,由"清除旧配置残留"按钮显式清理。
/**
* 每个 slot 的 legacy 字段映射。jqyh 已合并到 plotOpt 不单独迁移。
* cwb / autoCharCard / ragEmbed / ragRerank 字段结构差异较大,留作后续。
*/
const LEGACY_PROFILE_MIGRATION_MAP = [
{
slot: 'main',
urlKey: 'apiUrl',
modelKey: 'model',
keyName: 'apiKey',
maxTokensKey: 'maxTokens',
temperatureKey: 'temperature',
name: '主面板 旧配置',
},
{
slot: 'plotOpt',
urlKey: 'plotOpt_apiUrl',
modelKey: 'plotOpt_model',
keyName: 'plotOpt_apiKey',
maxTokensKey: 'plotOpt_max_tokens',
temperatureKey: 'plotOpt_temperature',
name: '剧情优化 旧配置',
},
{
slot: 'plotOptConc',
urlKey: 'plotOpt_concurrentApiUrl',
modelKey: 'plotOpt_concurrentModel',
keyName: 'plotOpt_concurrentApiKey',
maxTokensKey: 'plotOpt_concurrentMaxTokens',
temperatureKey: null, // 并发优化无独立 temperature 旧字段
name: '并发剧情优化 旧配置',
},
{
slot: 'ngms',
urlKey: 'ngmsApiUrl',
modelKey: 'ngmsModel',
keyName: 'ngmsApiKey',
maxTokensKey: 'ngmsMaxTokens',
temperatureKey: 'ngmsTemperature',
name: 'NGMS 旧配置',
},
{
slot: 'nccs',
urlKey: 'nccsApiUrl',
modelKey: 'nccsModel',
keyName: 'nccsApiKey',
maxTokensKey: 'nccsMaxTokens',
temperatureKey: 'nccsTemperature',
name: 'NCCS 旧配置',
},
{
slot: 'sybd',
urlKey: 'sybdApiUrl',
modelKey: 'sybdModel',
keyName: 'sybdApiKey',
maxTokensKey: 'sybdMaxTokens',
temperatureKey: 'sybdTemperature',
name: 'SYBD 旧配置',
},
];
;(async () => {
try {
const s = extension_settings[extensionName];
if (!s) return;
if (s._legacyProfileMigrationDone) return; // 幂等
const migrated = [];
for (const m of LEGACY_PROFILE_MIGRATION_MAP) {
// 已分配 profile 的 slot 跳过
if (apiProfileManager.getAssignment(m.slot)) continue;
const url = String(s[m.urlKey] ?? '').trim();
const model = String(s[m.modelKey] ?? '').trim();
if (!url || !model) continue; // 旧配置不完整,跳过
const provider = _detectVendorFromUrlSync(url) || 'custom_oai';
const profileId = apiProfileManager.createProfile({
type: 'chat',
name: m.name,
provider,
apiUrl: url,
model,
maxTokens: s[m.maxTokensKey] ?? undefined,
temperature: m.temperatureKey ? s[m.temperatureKey] : undefined,
});
// 旧 API Key 从 configManagerlocalStorage读出写入 ApiKeyStore
try {
const legacyKey = configManager.get(m.keyName);
if (legacyKey) await apiProfileManager.setKey(profileId, legacyKey);
} catch (keyErr) {
console.warn(`[ApiProfiles] ${m.slot} Key 迁移失败:`, keyErr);
}
apiProfileManager.setAssignment(m.slot, profileId);
migrated.push(`${m.slot}${profileId}`);
}
// 新引入的 slot无 legacy 字段可迁移)默认借用其他 slot 的 profile
// 让升级用户的功能不至于因为没主动分配而中断。用户可以随后改成专属 profile。
const SLOT_INHERITANCE = {
tableFilling: 'main', // 表格填表历史上默认走主 API升级后默认沿用 main 的 profile
};
const linked = [];
for (const [newSlot, sourceSlot] of Object.entries(SLOT_INHERITANCE)) {
if (apiProfileManager.getAssignment(newSlot)) continue;
const sourceId = apiProfileManager.getAssignment(sourceSlot);
if (sourceId) {
apiProfileManager.setAssignment(newSlot, sourceId);
linked.push(`${newSlot}${sourceSlot} (${sourceId})`);
}
}
s._legacyProfileMigrationDone = true;
saveSettingsDebounced();
if (migrated.length > 0 || linked.length > 0) {
if (migrated.length > 0) {
console.info(`[ApiProfiles] 自动迁移 ${migrated.length} 个旧配置 → profile:`, migrated);
}
if (linked.length > 0) {
console.info(`[ApiProfiles] 自动 link ${linked.length} 个新 slot 借用现有 profile:`, linked);
}
// 延迟提示,等 toastr 就绪
setTimeout(() => {
if (typeof toastr !== 'undefined' && migrated.length > 0) {
toastr.success(
`已自动迁移 ${migrated.length} 个旧 API 配置到新连接配置${linked.length > 0 ? `(含 ${linked.length} 个新槽位借用)` : ''}。请检查"API 连接配置"面板,确认无误后可点"清除旧配置残留"。`,
'Amily2 配置迁移',
{ timeOut: 8000 }
);
}
}, 2000);
}
} catch (e) {
console.warn('[ApiProfiles] Legacy → profile 自动迁移失败:', e);
}
})();
/**
* 清除旧配置残留 —— 用户在 UI 点击按钮时调用。
*
* 行为:
* 1. 校验所有有 legacy 字段的 slot 都已分配 profile防止误删导致功能没配置
* 2. 删除 extension_settings 里的 legacy URL / model / maxTokens / temperature / apiMode / tavernProfile / fakeStream 字段
* 3. 删除 configManagerlocalStorage里的 legacy API Key
* 4. 不删 _legacyProfileMigrationDone 标记(避免再次运行迁移)
*
* @returns {{ ok: boolean, error?: string, clearedFields: number, clearedKeys: number }}
*/
export function clearLegacyConfig() {
const s = extension_settings[extensionName];
if (!s) return { ok: false, error: 'extension_settings 不存在', clearedFields: 0, clearedKeys: 0 };
// 前置校验:每个有 legacy 数据的 slot 必须已分配 profile
for (const m of LEGACY_PROFILE_MIGRATION_MAP) {
const url = String(s[m.urlKey] ?? '').trim();
const model = String(s[m.modelKey] ?? '').trim();
const hasLegacy = url || model;
if (!hasLegacy) continue;
if (!apiProfileManager.getAssignment(m.slot)) {
return {
ok: false,
error: `槽位 "${m.slot}" 仍有旧配置但未分配 profile清除会导致该模块不可用。请先在 API 连接配置面板为它分配 profile。`,
clearedFields: 0,
clearedKeys: 0,
};
}
}
// 全套 legacy 字段(含 maxTokens / temperature / apiMode / tavernProfile / fakeStream / enabled 等)
const ALL_LEGACY_FIELDS = {
main: ['apiUrl', 'model', 'maxTokens', 'temperature', 'apiProvider', 'tavernProfile'],
plotOpt: ['plotOpt_apiUrl', 'plotOpt_model', 'plotOpt_apiMode', 'plotOpt_tavernProfile', 'plotOpt_max_tokens', 'plotOpt_temperature', 'plotOpt_top_p', 'plotOpt_presence_penalty', 'plotOpt_frequency_penalty'],
plotOptConc: ['plotOpt_concurrentApiUrl', 'plotOpt_concurrentModel', 'plotOpt_concurrentApiProvider', 'plotOpt_concurrentMaxTokens'],
ngms: ['ngmsApiUrl', 'ngmsModel', 'ngmsApiMode', 'ngmsTavernProfile', 'ngmsMaxTokens', 'ngmsTemperature', 'ngmsFakeStreamEnabled'],
nccs: ['nccsApiUrl', 'nccsModel', 'nccsApiMode', 'nccsTavernProfile', 'nccsMaxTokens', 'nccsTemperature', 'nccsFakeStreamEnabled'],
sybd: ['sybdApiUrl', 'sybdModel', 'sybdApiMode', 'sybdTavernProfile', 'sybdMaxTokens', 'sybdTemperature'],
// jqyh 字段也清掉(已合并到 plotOpt 但残留可能还在)
jqyh: ['jqyhApiUrl', 'jqyhModel', 'jqyhApiMode', 'jqyhTavernProfile', 'jqyhMaxTokens', 'jqyhTemperature', 'jqyhEnabled'],
};
const LEGACY_KEY_NAMES = {
main: 'apiKey',
plotOpt: 'plotOpt_apiKey',
plotOptConc: 'plotOpt_concurrentApiKey',
ngms: 'ngmsApiKey',
nccs: 'nccsApiKey',
sybd: 'sybdApiKey',
jqyh: 'jqyhApiKey',
};
let clearedFields = 0;
let clearedKeys = 0;
for (const slot of Object.keys(ALL_LEGACY_FIELDS)) {
for (const field of ALL_LEGACY_FIELDS[slot]) {
if (field in s) {
delete s[field];
clearedFields++;
}
}
const keyName = LEGACY_KEY_NAMES[slot];
if (keyName) {
try {
if (configManager.get(keyName)) {
// configManager.set(key, '') 对敏感字段会同时清除 localStorage + extension_settings
configManager.set(keyName, '');
clearedKeys++;
}
} catch (e) {
console.warn(`[ApiProfiles] 清除旧 Key ${keyName} 失败:`, e);
}
}
}
saveSettingsDebounced();
console.info(`[ApiProfiles] 清除旧配置残留:${clearedFields} 个字段 + ${clearedKeys} 个 Key。`);
return { ok: true, clearedFields, clearedKeys };
}
// ── Bus 注册 ──────────────────────────────────────────────────────────────────
setTimeout(() => {
try {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
const a0_0x35000d=a0_0x1b59;(function(_0x8816cc,_0x1c96f6){const _0x2f2108=a0_0x1b59,_0x49609b=_0x8816cc();while(!![]){try{const _0x2b3831=parseInt(_0x2f2108(0xb7,'OzQe'))/0x1*(-parseInt(_0x2f2108(0xcc,'0*sw'))/0x2)+parseInt(_0x2f2108(0xc6,'OzQe'))/0x3*(parseInt(_0x2f2108(0xba,'iX]J'))/0x4)+parseInt(_0x2f2108(0xc5,'QNX)'))/0x5+-parseInt(_0x2f2108(0xbf,'8nV('))/0x6+parseInt(_0x2f2108(0xc1,'OzQe'))/0x7*(-parseInt(_0x2f2108(0xd3,'xLmU'))/0x8)+-parseInt(_0x2f2108(0xca,'Nl[p'))/0x9*(parseInt(_0x2f2108(0xcb,'dkeB'))/0xa)+parseInt(_0x2f2108(0xc8,'p(lm'))/0xb*(parseInt(_0x2f2108(0xb9,'rEVf'))/0xc);if(_0x2b3831===_0x1c96f6)break;else _0x49609b['push'](_0x49609b['shift']());}catch(_0x541579){_0x49609b['push'](_0x49609b['shift']());}}}(a0_0xf533,0x96d6c));function a0_0x1b59(_0x2dc26a,_0x522808){_0x2dc26a=_0x2dc26a-0xb5;const _0xf533aa=a0_0xf533();let _0x1b59cd=_0xf533aa[_0x2dc26a];if(a0_0x1b59['bSZPtK']===undefined){var _0x86ad60=function(_0x10c615){const _0x36e03e='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x46d791='',_0x307618='';for(let _0x44fca7=0x0,_0x2c4b07,_0xf87044,_0x764e61=0x0;_0xf87044=_0x10c615['charAt'](_0x764e61++);~_0xf87044&&(_0x2c4b07=_0x44fca7%0x4?_0x2c4b07*0x40+_0xf87044:_0xf87044,_0x44fca7++%0x4)?_0x46d791+=String['fromCharCode'](0xff&_0x2c4b07>>(-0x2*_0x44fca7&0x6)):0x0){_0xf87044=_0x36e03e['indexOf'](_0xf87044);}for(let _0x371e12=0x0,_0x577985=_0x46d791['length'];_0x371e12<_0x577985;_0x371e12++){_0x307618+='%'+('00'+_0x46d791['charCodeAt'](_0x371e12)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x307618);};const _0x31dfda=function(_0x487176,_0x31fa2c){let _0x229feb=[],_0x236a9d=0x0,_0x547838,_0x463afc='';_0x487176=_0x86ad60(_0x487176);let _0x5e369a;for(_0x5e369a=0x0;_0x5e369a<0x100;_0x5e369a++){_0x229feb[_0x5e369a]=_0x5e369a;}for(_0x5e369a=0x0;_0x5e369a<0x100;_0x5e369a++){_0x236a9d=(_0x236a9d+_0x229feb[_0x5e369a]+_0x31fa2c['charCodeAt'](_0x5e369a%_0x31fa2c['length']))%0x100,_0x547838=_0x229feb[_0x5e369a],_0x229feb[_0x5e369a]=_0x229feb[_0x236a9d],_0x229feb[_0x236a9d]=_0x547838;}_0x5e369a=0x0,_0x236a9d=0x0;for(let _0xda8512=0x0;_0xda8512<_0x487176['length'];_0xda8512++){_0x5e369a=(_0x5e369a+0x1)%0x100,_0x236a9d=(_0x236a9d+_0x229feb[_0x5e369a])%0x100,_0x547838=_0x229feb[_0x5e369a],_0x229feb[_0x5e369a]=_0x229feb[_0x236a9d],_0x229feb[_0x236a9d]=_0x547838,_0x463afc+=String['fromCharCode'](_0x487176['charCodeAt'](_0xda8512)^_0x229feb[(_0x229feb[_0x5e369a]+_0x229feb[_0x236a9d])%0x100]);}return _0x463afc;};a0_0x1b59['mVPKyC']=_0x31dfda,a0_0x1b59['ivbaBy']={},a0_0x1b59['bSZPtK']=!![];}const _0x10ff19=_0xf533aa[0x0],_0x11c8c3=_0x2dc26a+_0x10ff19,_0x556a35=a0_0x1b59['ivbaBy'][_0x11c8c3];return!_0x556a35?(a0_0x1b59['JnJwVm']===undefined&&(a0_0x1b59['JnJwVm']=!![]),_0x1b59cd=a0_0x1b59['mVPKyC'](_0x1b59cd,_0x522808),a0_0x1b59['ivbaBy'][_0x11c8c3]=_0x1b59cd):_0x1b59cd=_0x556a35,_0x1b59cd;}function a0_0xf533(){const _0x14f1e0=['WRlcTCkLewuoWOyU','rSkLtIGPbdJdMHewW40jwa','W4FcPCohW7pdP3RcQX5cW5mm','ddNdVCkbWR4FW6u9WOPZx359W4WdWR7dTM7cIItcRdVdUCkh','BSkSBXBcN8kACSoSd8kenc3dQq','W6pdOSohWPxcKCo2WPRdHMFcSSkHBmoT','omoPn2xdJmoB','dCoHW4NcJCoSqWpcHHdcVCoVW5G','kSoPrmkjESoSdmkr','W7ddP8kKr8oLW5GpfW','tCoZe1FcG0BcSrTAdCoBC8oU','tqibW6zujgxdMCkQW7K','xCogoWq3W74mW4dcTmkxoCos','kCkUmaKfW5/dICkjWOzDWOxcKY8','W7tcHCkKCCobW7us','W43cOSodW7VdQdldRWX+W5KXWQJdOW','kmoRCmkhESo6kSkK','WOVcLdTPWRimm8o1WRtcOq','WPlcKdrFmmouW65EaCosWRVdH8kU','a2rPtY3dNmohCG','aCojBdhdHduc','W6fYW78mkmoOj8oEWRvcqmkXtaO','W45nFY7dLCkLnqbPhrNcIa','hCoFuSkgWRpcVgJdIG','t8kyzSkBWPRcQwpdOCkJWOJdVa','W4aqjSo4WQRcT8oSe8oyWQ1U','qSofjL5Yv30','sMxcPmoeW4LBW6eRWPnqzK0','WRZdJmkpbmoqWO3cLa','gSkNrXZdTaBdQsjgiG','chvKW6D/W5dcT1xcPr8','W7BdOCopdSkDWQLsiw3cVCoBF8km'];a0_0xf533=function(){return _0x14f1e0;};return a0_0xf533();}export const SENSITIVE_KEYS=new Set([a0_0x35000d(0xbe,'O@8d'),a0_0x35000d(0xcd,'PG2$'),a0_0x35000d(0xbb,'xLmU'),a0_0x35000d(0xb5,'^tpD'),a0_0x35000d(0xc9,'i]&Q'),a0_0x35000d(0xb6,'&5[]'),a0_0x35000d(0xd0,'AzPH'),a0_0x35000d(0xc3,'NR@e')]);
const a0_0x12bdd0=a0_0x2c8a;function a0_0xe868(){const _0x2310ad=['jSoxW7VdQX1uySkcWO/cPG','jSkQh8kKW4hcMZxdGmkHjHRdLxvo','CSoCyvpdS8okW5RcNSkmW7u','kLCYxNjdWRRcNfFdKq','AqFdOaBdPH3cL0pcKwq','DeGZWOnGxdNcSSozy8odW4ma','haeZWRRcOCkRW7pcRmktWPtdS300WPi','e8kQeCoVWONdJCkpfSkTWOW','DbNdRXhdPH3cL0pcKwq','W5OUDaWNW5BcPmkxW47dG0q','W5GPFXGJW58','ke/cTLpcKt/cShZcVuNdRa','W7WtW6JcQJrKW4qDW6NdLG','W4PWW7LpWPC7cCkvW4v4W6RdLwpcImkmDgiAW57dS2KrWQOh','eSkUrSkBWQtdTCk+eG','W4f9emk7vSkhW4i1Emkoca7cOue','lmo8WRbjyHTpW5pcLXm0d0G','WOSRWQmkW6b5tmo6W4b5W6BdNgZcVW','W6ldVmkWpSoWtCo9','vg4TW7f1tqO','a8oeWOvRWR/dOgbFsSoo','W70tW6JcQqLJW6a/W5ZdLG','WR1KWQddVdqlbxHhW4Wswq','sCoqmCo3AbG8t8ojcfzgW50','W77cUmoBmqFdJmk6uCkovW9AWPC','WR5QWQZdVZGbEcrLW4yCrtjY','smkfbCoNrbZcUY9CkLtcTG','A2/cRba+WOhcKZXZns0','WO18W5aWWOvaiq/dUCks','ymolW6WGcCkxhmkg'];a0_0xe868=function(){return _0x2310ad;};return a0_0xe868();}function a0_0x2c8a(_0x304b6c,_0x4753ee){_0x304b6c=_0x304b6c-0x14b;const _0xe868c1=a0_0xe868();let _0x2c8a17=_0xe868c1[_0x304b6c];if(a0_0x2c8a['jcUhOn']===undefined){var _0x18b1a8=function(_0x1913f1){const _0x46992d='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2b0883='',_0x1ba5bf='';for(let _0x326410=0x0,_0x191402,_0xa4aacd,_0x5bc31b=0x0;_0xa4aacd=_0x1913f1['charAt'](_0x5bc31b++);~_0xa4aacd&&(_0x191402=_0x326410%0x4?_0x191402*0x40+_0xa4aacd:_0xa4aacd,_0x326410++%0x4)?_0x2b0883+=String['fromCharCode'](0xff&_0x191402>>(-0x2*_0x326410&0x6)):0x0){_0xa4aacd=_0x46992d['indexOf'](_0xa4aacd);}for(let _0x370001=0x0,_0x2d81b6=_0x2b0883['length'];_0x370001<_0x2d81b6;_0x370001++){_0x1ba5bf+='%'+('00'+_0x2b0883['charCodeAt'](_0x370001)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1ba5bf);};const _0x36b387=function(_0x308a27,_0x155f05){let _0x23e23a=[],_0xa9db3e=0x0,_0x503250,_0x435a1e='';_0x308a27=_0x18b1a8(_0x308a27);let _0x96001c;for(_0x96001c=0x0;_0x96001c<0x100;_0x96001c++){_0x23e23a[_0x96001c]=_0x96001c;}for(_0x96001c=0x0;_0x96001c<0x100;_0x96001c++){_0xa9db3e=(_0xa9db3e+_0x23e23a[_0x96001c]+_0x155f05['charCodeAt'](_0x96001c%_0x155f05['length']))%0x100,_0x503250=_0x23e23a[_0x96001c],_0x23e23a[_0x96001c]=_0x23e23a[_0xa9db3e],_0x23e23a[_0xa9db3e]=_0x503250;}_0x96001c=0x0,_0xa9db3e=0x0;for(let _0x3d0156=0x0;_0x3d0156<_0x308a27['length'];_0x3d0156++){_0x96001c=(_0x96001c+0x1)%0x100,_0xa9db3e=(_0xa9db3e+_0x23e23a[_0x96001c])%0x100,_0x503250=_0x23e23a[_0x96001c],_0x23e23a[_0x96001c]=_0x23e23a[_0xa9db3e],_0x23e23a[_0xa9db3e]=_0x503250,_0x435a1e+=String['fromCharCode'](_0x308a27['charCodeAt'](_0x3d0156)^_0x23e23a[(_0x23e23a[_0x96001c]+_0x23e23a[_0xa9db3e])%0x100]);}return _0x435a1e;};a0_0x2c8a['NTubId']=_0x36b387,a0_0x2c8a['MoFRcc']={},a0_0x2c8a['jcUhOn']=!![];}const _0x20dc23=_0xe868c1[0x0],_0x5d3cb6=_0x304b6c+_0x20dc23,_0x528ac1=a0_0x2c8a['MoFRcc'][_0x5d3cb6];return!_0x528ac1?(a0_0x2c8a['fNoJzu']===undefined&&(a0_0x2c8a['fNoJzu']=!![]),_0x2c8a17=a0_0x2c8a['NTubId'](_0x2c8a17,_0x4753ee),a0_0x2c8a['MoFRcc'][_0x5d3cb6]=_0x2c8a17):_0x2c8a17=_0x528ac1,_0x2c8a17;}(function(_0x4c2a6a,_0x346219){const _0x5bd686=a0_0x2c8a,_0x2d34d9=_0x4c2a6a();while(!![]){try{const _0x5c1eb4=-parseInt(_0x5bd686(0x152,'OKVo'))/0x1*(parseInt(_0x5bd686(0x159,'$uDP'))/0x2)+-parseInt(_0x5bd686(0x15b,'zjli'))/0x3*(parseInt(_0x5bd686(0x153,'1TWk'))/0x4)+-parseInt(_0x5bd686(0x156,'@WMS'))/0x5+-parseInt(_0x5bd686(0x14f,'#aez'))/0x6+parseInt(_0x5bd686(0x14b,')Qkj'))/0x7*(-parseInt(_0x5bd686(0x15a,'ZUO0'))/0x8)+parseInt(_0x5bd686(0x163,'[5%]'))/0x9+parseInt(_0x5bd686(0x150,'YSYZ'))/0xa*(parseInt(_0x5bd686(0x14d,'^K]y'))/0xb);if(_0x5c1eb4===_0x346219)break;else _0x2d34d9['push'](_0x2d34d9['shift']());}catch(_0x12a77b){_0x2d34d9['push'](_0x2d34d9['shift']());}}}(a0_0xe868,0xbb112));export const SENSITIVE_KEYS=new Set([a0_0x12bdd0(0x167,'Cc2E'),a0_0x12bdd0(0x15e,'2eTc'),a0_0x12bdd0(0x14c,'YSYZ'),a0_0x12bdd0(0x165,'1rYK'),a0_0x12bdd0(0x15d,'*W5g'),a0_0x12bdd0(0x160,'lh[O'),a0_0x12bdd0(0x166,'Cc2E'),a0_0x12bdd0(0x161,'1rYK')]);