UNPKG

node-karin

Version:

Lightweight, efficient, concise, and stable robot framework.

1,483 lines (1,475 loc) 673 kB
import chalk2 from 'chalk'; import util4, { promisify } from 'node:util'; import path from 'path'; import { fileURLToPath } from 'url'; import fs5, { promises, existsSync as existsSync$1, createWriteStream } from 'node:fs'; import log4js from 'log4js'; import YAML, { isMap, isSeq, isPair } from 'yaml'; import path4 from 'node:path'; import chokidar from 'chokidar'; import { pipeline, Readable } from 'node:stream'; import axios10, { AxiosError } from 'axios'; import lodash3 from 'lodash'; import { URL as URL$1, fileURLToPath as fileURLToPath$1, pathToFileURL } from 'node:url'; import { createRequire } from 'module'; import crypto, { createHmac, randomUUID } from 'node:crypto'; import EventEmitter2, { EventEmitter } from 'node:events'; import { isPromise } from 'util/types'; import { exec as exec$1, spawn } from 'child_process'; import os from 'node:os'; import { lookup } from 'dns/promises'; import sqlite3 from 'sqlite3'; import WebSocket, { WebSocketServer, WebSocket as WebSocket$1 } from 'ws'; import jwt from 'jsonwebtoken'; import dotenv from 'dotenv'; import template from 'art-template'; import schedule from 'node-schedule'; import moment2 from 'moment'; import express2, { Router } from 'express'; import { createServer } from 'node:http'; import { createBrotliDecompress } from 'node:zlib'; import { createClient } from 'redis'; var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var time, debugEnabled, isDebugMode, createDebug; var init_debug = __esm({ "src/utils/debug/debug.ts"() { time = Date.now(); debugEnabled = false; isDebugMode = () => { return debugEnabled || typeof process.env.DEBUG === "string" || process.argv.some((arg) => arg.includes("debug")); }; createDebug = (prefix) => { let color = (text2) => chalk2.hex("#0cafff")(text2); const setColor = (fnc2) => { color = fnc2; }; const debugFn = (...args) => { process.stderr.write(`${color(prefix)} ${util4.format(...args)} ${color(`+${Date.now() - time}ms`)} `); }; const noop = () => { }; let debug3 = isDebugMode() ? debugFn : noop; return Object.assign(debug3, { chalk: chalk2, setColor, enable: (bool) => { debugEnabled = bool; debug3 = isDebugMode() ? debugFn : noop; } }); }; } }); // src/service/debug.ts var debug2; var init_debug2 = __esm({ "src/service/debug.ts"() { init_debug(); debug2 = createDebug("core"); global.debug = debug2; } }); var filename, karinPathRoot, karinPathMain, karinPathPlugins, isPackaged, karinPathDefaultConfig, karinPathDefaultView, karinPathComment, karinPathBase, karinPathConfig, karinPathData, karinPathTemp, karinPathResource, karinPathDb, karinPathRedisSqlite3, karinPathKv, karinPathLogs, karinPathHtml, karinPathPm2Config, karinPathConsole, karinPathSandboxData, karinPathSandboxTemp, karinDir, karinMain, pluginDir, isPkg, defaultConfigPath, defaultViewPath, commentPath, basePath, configPath, dataPath, tempPath, resourcePath, dbPath, redisSqlite3Path, kvPath, logsPath, htmlPath, pm2Path, consolePath, sandboxDataPath, sandboxTempPath, root_default; var init_root = __esm({ "src/root.ts"() { filename = fileURLToPath(import.meta.url); karinPathRoot = Object.freeze(path.join(filename, "../..").replace(/\\/g, "/")); karinPathMain = Object.freeze(path.join(karinPathRoot, "dist", "index.js")); karinPathPlugins = Object.freeze(path.join(process.cwd(), "plugins")); isPackaged = Object.freeze(filename.includes("node_modules")); karinPathDefaultConfig = Object.freeze(path.join(karinPathRoot, "default", "config")); karinPathDefaultView = Object.freeze(path.join(karinPathRoot, "default", "view")); karinPathComment = Object.freeze(path.join(karinPathRoot, "default", "comment")); karinPathBase = Object.freeze(path.join(process.cwd(), "@karinjs")); karinPathConfig = Object.freeze(path.join(karinPathBase, "config")); karinPathData = Object.freeze(path.join(karinPathBase, "data")); karinPathTemp = Object.freeze(path.join(karinPathBase, "temp")); karinPathResource = Object.freeze(path.join(karinPathBase, "resource")); karinPathDb = Object.freeze(path.join(karinPathData, "db")); karinPathRedisSqlite3 = Object.freeze(path.join(karinPathDb, "redis-sqlite3")); karinPathKv = Object.freeze(path.join(karinPathDb, "kv")); karinPathLogs = Object.freeze(path.join(karinPathBase, "logs")); karinPathHtml = Object.freeze(path.join(karinPathTemp, "html")); karinPathPm2Config = Object.freeze(path.join(karinPathConfig, "pm2.yaml")); karinPathConsole = Object.freeze(path.join(karinPathTemp, "console")); karinPathSandboxData = Object.freeze(path.join(karinPathData, "sandbox")); karinPathSandboxTemp = Object.freeze(path.join(karinPathTemp, "sandbox")); karinDir = karinPathRoot; karinMain = karinPathMain; pluginDir = karinPathPlugins; isPkg = isPackaged; defaultConfigPath = karinPathDefaultConfig; defaultViewPath = karinPathDefaultView; commentPath = karinPathComment; basePath = karinPathBase; configPath = karinPathConfig; dataPath = karinPathData; tempPath = karinPathTemp; resourcePath = karinPathResource; dbPath = karinPathDb; redisSqlite3Path = karinPathRedisSqlite3; kvPath = karinPathKv; logsPath = karinPathLogs; htmlPath = karinPathHtml; pm2Path = karinPathPm2Config; consolePath = karinPathConsole; sandboxDataPath = karinPathSandboxData; sandboxTempPath = karinPathSandboxTemp; root_default = { // 新常量 karinPathRoot, karinPathMain, karinPathPlugins, isPackaged, karinPathDefaultConfig, karinPathDefaultView, karinPathComment, karinPathBase, karinPathConfig, karinPathData, karinPathTemp, karinPathResource, karinPathDb, karinPathRedisSqlite3, karinPathKv, karinPathLogs, karinPathHtml, karinPathPm2Config, karinPathConsole, karinPathSandboxData, karinPathSandboxTemp, // 旧常量,保持兼容性 karinDir, karinMain, pluginDir, isPkg, defaultConfigPath, defaultViewPath, commentPath, basePath, configPath, dataPath, tempPath, resourcePath, dbPath, redisSqlite3Path, kvPath, logsPath, htmlPath, pm2Path, consolePath, sandboxDataPath, sandboxTempPath }; } }); var initLogger, addColor, createLogger, createInnerLogger; var init_logger = __esm({ "src/utils/logger/logger.ts"() { initLogger = (options = {}) => { if (options.config) return log4js.configure(options.config); const { level = "info", daysToKeep = 14, maxLogSize = 0 } = options.log4jsCfg || {}; const config2 = { appenders: { console: { type: "console", layout: { type: "pattern", pattern: `%[[Karin][%d{hh:mm:ss.SSS}][%4.4p]%] ${process.env.RUNTIME === "tsx" ? "[%f{3}:%l] " : ""}%m` } }, overall: { /** 输出到文件 */ type: "file", /** 日志文件名 */ filename: "@karinjs/logs/logger", /** 日期后缀 */ pattern: "yyyy-MM-dd.log", /** 日期后缀 */ keepFileExt: true, /** 日志文件名中包含日期模式 */ alwaysIncludePattern: true, /** 日志文件保留天数 */ daysToKeep: daysToKeep || 14, /** 日志输出格式 */ layout: { type: "pattern", pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m" } } }, categories: { default: { appenders: ["overall", "console"], level, enableCallStack: process.env.RUNTIME === "tsx" } }, levels: { handler: { value: 15e3, colour: "cyan" } } }; if (maxLogSize > 0) { config2.categories.default.appenders.unshift("fragments"); config2.appenders.fragments = { type: "file", filename: "@karinjs/logs/app.log", pattern: "MM-dd.log", keepFileExt: true, alwaysIncludePattern: true, daysToKeep: daysToKeep || 14, maxLogSize: (maxLogSize || 30) * 1024 * 1024, /** 最大文件数 */ numBackups: 9999999, /** 日志输出格式 */ layout: { type: "pattern", pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m" } }; } return log4js.configure(config2); }; addColor = (Logger, color) => { const logger3 = Logger; logger3.chalk = chalk2; logger3.red = chalk2.red; logger3.green = chalk2.green; logger3.yellow = chalk2.yellow; logger3.blue = chalk2.blue; logger3.magenta = chalk2.magenta; logger3.cyan = chalk2.cyan; logger3.white = chalk2.white; logger3.gray = chalk2.gray; logger3.violet = chalk2.hex("#868ECC"); logger3.fnc = chalk2.hex(color || "#FFFF00"); logger3.bot = (level, id, ...args) => { switch (level) { case "trace": return logger3.trace(logger3.violet(`[Bot:${id}]`), ...args); case "debug": return logger3.debug(logger3.violet(`[Bot:${id}]`), ...args); case "mark": return logger3.mark(logger3.violet(`[Bot:${id}]`), ...args); case "info": return logger3.info(logger3.violet(`[Bot:${id}]`), ...args); case "warn": return logger3.warn(logger3.violet(`[Bot:${id}]`), ...args); case "error": return logger3.error(logger3.violet(`[Bot:${id}]`), ...args); case "fatal": return logger3.fatal(logger3.violet(`[Bot:${id}]`), ...args); default: return logger3.info(logger3.violet(`[Bot:${id}]`), ...args); } }; return logger3; }; createLogger = (dir2, options = {}) => { if (!fs5.existsSync(dir2)) { fs5.mkdirSync(dir2, { recursive: true }); } initLogger(options); const logger3 = addColor(log4js.getLogger("default")); global.logger = logger3; return logger3; }; createInnerLogger = (dir2) => { return createLogger(dir2, { log4jsCfg: { level: process.env.LOG_LEVEL || "info", daysToKeep: Number(process.env.LOG_DAYS_TO_KEEP) || 30, maxLogSize: Number(process.env.LOG_MAX_LOG_SIZE) || 0, logColor: process.env.LOG_FNC_COLOR || "#E1D919" } }); }; } }); // src/utils/logger/index.ts var init_logger2 = __esm({ "src/utils/logger/index.ts"() { init_logger(); } }); // src/env/index.ts var isWin, isDev, isTsx, isTs, setProcessEnv, setVersion, setRuntime; var init_env = __esm({ "src/env/index.ts"() { isWin = () => process.platform === "win32"; isDev = () => process.env.NODE_ENV === "development"; isTsx = () => process.env.RUNTIME === "tsx"; isTs = () => isTsx(); setProcessEnv = (key, value) => { process.env[key] = value + ""; return value; }; setVersion = (version) => setProcessEnv("KARIN_VERSION", version); setRuntime = (runtime) => setProcessEnv("RUNTIME", runtime); } }); var cache, clearRequireFile, clearRequire, requireFile, requireFileSync, fileReady, fileCache, touchRequireFile, parseContent; var init_require = __esm({ "src/utils/fs/require.ts"() { cache = /* @__PURE__ */ new Map(); clearRequireFile = (filePath) => { const absPath2 = path4.resolve(filePath).replace(/\\/g, "/"); return cache.has(absPath2) && cache.delete(absPath2); }; clearRequire = () => cache.clear(); requireFile = async (filePath, options = {}) => { const now = Date.now(); const absPath2 = path4.resolve(filePath).replace(/\\/g, "/"); const { encoding = "utf-8", force = false, ex = 300, size = 0, parser: parser2, type, readCache } = options; const data = fileReady(absPath2, now, force, ex, readCache); if (data !== false) return data; const content = await fs5.promises.readFile(absPath2, encoding); return fileCache(content, absPath2, ex, now, size, encoding, parser2, type); }; requireFileSync = (filePath, options = {}) => { const now = Date.now(); const absPath2 = path4.resolve(filePath).replace(/\\/g, "/"); const { encoding = "utf-8", force = false, ex = 300, size = 0, parser: parser2, type, readCache } = options; const data = fileReady(absPath2, now, force, ex, readCache); if (data !== false) return data; const content = fs5.readFileSync(absPath2, encoding); return fileCache(content, absPath2, ex, now, size, encoding, parser2, type); }; fileReady = (absPath2, now, force, ex, readCache) => { if (!force) { const cached = cache.get(absPath2); if (cached) { if (cached.expiry === 0 || cached.expiry > now) { if (ex <= 0) return cached.data; touchRequireFile(absPath2, ex); return cached.data; } else { cache.delete(absPath2); } } if (readCache) return void 0; } return false; }; fileCache = (content, absPath2, ex, now, size, encoding, parser2, type) => { if (size > 0 && Buffer.byteLength(content, encoding) > size) { return parseContent(absPath2, content, parser2, type); } const data = parseContent(absPath2, content, parser2, type); const expiry = ex === 0 ? 0 : now + ex * 1e3; cache.set(absPath2, { data, expiry }); return data; }; touchRequireFile = async (filePath, ex) => { const entry = cache.get(filePath); if (entry) { entry.expiry = Date.now() + ex * 1e3; cache.set(filePath, entry); } }; parseContent = (absPath2, content, parser2, type) => { if (parser2) return parser2(content); if (type) { if (type === "json") return JSON.parse(content); if (type === "yaml" || type === "yml") return YAML.parse(content); } if (absPath2.endsWith(".json")) return JSON.parse(content); if (absPath2.endsWith(".yaml") || absPath2.endsWith(".yml")) return YAML.parse(content); return content; }; setInterval(() => { const now = Date.now(); for (const [key, entry] of cache.entries()) { if (entry.expiry === 0) continue; if (entry.expiry <= now) cache.delete(key); } }, 6e4); } }); // src/utils/config/pkg.ts var pkg; var init_pkg = __esm({ "src/utils/config/pkg.ts"() { init_root(); init_require(); pkg = () => requireFileSync(`${karinDir}/package.json`); } }); // src/utils/config/default.ts var defaultConfig; var init_default = __esm({ "src/utils/config/default.ts"() { defaultConfig = Object.freeze({ adapter: { console: { isLocal: true, token: "", host: "" }, onebot: { ws_server: { enable: true, timeout: 120 }, ws_client: [ { enable: false, url: "ws://127.0.0.1:7778", token: "" } ], http_server: [ { enable: false, self_id: "default", url: "http://127.0.0.1:6099", token: "", api_token: "", post_token: "" } ] } }, config: { master: [ "console" ], admin: [], user: { enable_list: [], disable_list: [] }, friend: { enable: true, enable_list: [], disable_list: [], log_enable_list: [], log_disable_list: [] }, group: { enable: true, enable_list: [], disable_list: [], log_enable_list: [], log_disable_list: [] }, directs: { enable: true, enable_list: [], disable_list: [], log_enable_list: [], log_disable_list: [] }, guilds: { enable: true, enable_list: [], disable_list: [], log_enable_list: [], log_disable_list: [] }, channels: { enable: true, enable_list: [], disable_list: [], log_enable_list: [], log_disable_list: [] } }, groups: [ { key: "default", cd: 0, userCD: 0, mode: 0, alias: [], enable: [], disable: [], member_enable: [], member_disable: [] }, { key: "Bot:selfId", cd: 0, userCD: 0, mode: 0, alias: [], enable: [], disable: [], member_enable: [], member_disable: [] }, { key: "Bot:selfId:groupId", cd: 0, userCD: 0, mode: 0, alias: [], enable: [], disable: [], member_enable: [], member_disable: [] }, { key: "Bot:selfId:guildId", cd: 0, userCD: 0, mode: 0, alias: [], enable: [], disable: [], member_enable: [], member_disable: [] }, { key: "Bot:selfId:guildId:channelId", cd: 0, userCD: 0, mode: 0, alias: [], enable: [], disable: [], member_enable: [], member_disable: [] } ], pm2: { lines: 1e3, apps: [ { name: "karin", script: "index.js", autorestart: true, max_restarts: 60, max_memory_restart: "1G", restart_delay: 2e3, merge_logs: true, error_file: "./@karinjs/logs/pm2_error.log", out_file: "./@karinjs/logs/pm2_out.log" } ] }, privates: [ { key: "default", cd: 0, mode: 0, alias: [], enable: [], disable: [] }, { key: "Bot:selfId", cd: 0, mode: 0, alias: [], enable: [], disable: [] }, { key: "Bot:selfId:userId", cd: 0, mode: 0, alias: [], enable: [], disable: [] } ], redis: { url: "redis://127.0.0.1:6379", username: "", password: "", database: 0 }, render: { ws_server: { enable: true }, ws_client: [ { enable: false, url: "ws://127.0.0.1:7005", token: "123456", isSnapka: false, reconnectTime: 5e3, heartbeatTime: 3e4 } ], http_server: [ { enable: false, url: "http://127.0.0.1:7005", token: "123456", isSnapka: false } ] } }); } }); var cache2, watch, watchAndMerge, Watch, Watcher; var init_watch = __esm({ "src/utils/fs/watch.ts"() { init_require(); cache2 = /* @__PURE__ */ new Map(); watch = (file, fnc2, options) => { const isWatch = cache2.get(file); if (isWatch) { isWatch.close(); cache2.delete(file); } const watcher = chokidar.watch(file); cache2.set(file, watcher); watcher.on("change", async () => { logger.info(`[\u914D\u7F6E\u6587\u4EF6\u53D8\u52A8] ${path4.relative(process.cwd(), file).replace(/\\/g, "/")}`); const oldData = requireFileSync(file, { ...options, readCache: true }); clearRequireFile(file); const newData = requireFileSync(file, { ...options, force: true }); typeof fnc2 === "function" && fnc2(oldData, newData); }); watcher.on("unlink", () => { logger.info(`[\u914D\u7F6E\u6587\u4EF6\u5220\u9664] ${path4.relative(process.cwd(), file).replace(/\\/g, "/")}`); clearRequireFile(file); }); watcher.once("close", () => { watcher.removeAllListeners(); cache2.delete(file); clearRequireFile(file); }); return new Watch(file, watcher, options); }; watchAndMerge = (dynamicFile, defaultCFile, fnc2) => { const watcher = watch(dynamicFile, fnc2); return new Watcher(dynamicFile, defaultCFile, watcher.watcher, watcher.options); }; Watch = class { watcher; file; options; constructor(file, watcher, options) { this.watcher = watcher; this.file = file; this.options = options; } /** * @description 获取配置数据 */ get value() { return requireFileSync(this.file, this.options); } /** * @description 关闭监听器并清理全部缓存 */ close() { clearRequireFile(this.file); return this.watcher.close(); } }; Watcher = class { watcher; dynamicFile; defaultCFile; options; constructor(dynamicFile, defaultCfgFile, watcher, options) { this.watcher = watcher; this.dynamicFile = dynamicFile; this.defaultCFile = defaultCfgFile; this.options = options; } /** * @description 获取配置数据 */ get value() { const dynamicData = requireFileSync(this.dynamicFile, this.options); const defaultData = requireFileSync(this.defaultCFile, this.options); if (typeof defaultData === "object" && typeof dynamicData === "object") { return { ...defaultData, ...dynamicData }; } if (Array.isArray(defaultData) && Array.isArray(dynamicData)) { return [...defaultData, ...dynamicData]; } return dynamicData; } /** * @description 关闭监听器并清理全部缓存 */ close() { clearRequireFile(this.defaultCFile); return this.watcher.close(); } }; } }); var exists, isDir, isFile, mkdir, existToMkdir; var init_fsPromises = __esm({ "src/utils/fs/fsPromises.ts"() { exists = async (file) => { try { await fs5.promises.access(file, fs5.constants.F_OK); return true; } catch { return false; } }; isDir = async (file) => { try { const stat = await fs5.promises.stat(file); return stat.isDirectory(); } catch { return false; } }; isFile = async (file) => { try { const stat = await fs5.promises.stat(file); return stat.isFile(); } catch { return false; } }; mkdir = async (dirname) => { try { await fs5.promises.mkdir(dirname, { recursive: true }); return true; } catch { return false; } }; existToMkdir = async (file) => { try { if (!await exists(file)) await mkdir(file); return true; } catch { return false; } }; } }); var existsSync, isDirSync, isFileSync, mkdirSync, existToMkdirSync, rmSync; var init_fsSync = __esm({ "src/utils/fs/fsSync.ts"() { existsSync = (file) => { return fs5.existsSync(file); }; isDirSync = (file) => { try { return fs5.statSync(file).isDirectory(); } catch { return false; } }; isFileSync = (file) => { try { return fs5.statSync(file).isFile(); } catch { return false; } }; mkdirSync = (dirname) => { try { fs5.mkdirSync(dirname, { recursive: true }); return true; } catch { return false; } }; existToMkdirSync = (file) => { try { if (!fs5.existsSync(file)) mkdirSync(file); return true; } catch { return false; } }; rmSync = fs5.rmSync; } }); var streamPipeline, sep, downFile, absPath, createPluginDir, getFiles, copyFilesSync, copyFiles, copyConfigSync, copyConfig, getAllFilesSync, getAllFiles; var init_file = __esm({ "src/utils/fs/file.ts"() { init_root(); streamPipeline = promisify(pipeline); sep = path4.sep === "/" ? /^file:\/\// : /^file:[/]{2,3}/; downFile = async (fileUrl, savePath, param = {}) => { try { await fs5.promises.mkdir(path4.dirname(savePath), { recursive: true }); const response = await axios10.get(fileUrl, { ...param, responseType: "stream" }); await streamPipeline(response.data, fs5.createWriteStream(savePath)); return true; } catch (error) { if (error instanceof AxiosError) { logger.error(`\u4E0B\u8F7D\u6587\u4EF6\u9519\u8BEF\uFF1A${error.stack}`); } else { logger.error(`\u4E0B\u8F7D\u6587\u4EF6\u9519\u8BEF\uFF1A${error}`); } return false; } }; absPath = (file, absPath2 = true, prefix = false) => { file = file.replace(/\\/g, "/"); if (file.startsWith("file://")) file = file.replace(sep, ""); file = path4.normalize(file); if (absPath2) file = path4.resolve(file); if (prefix) file = "file://" + file; return file.replace(/\\/g, "/"); }; createPluginDir = async (name, files) => { if (!Array.isArray(files)) files = ["config", "data", "resources"]; if (files.length === 0) return; const pluginPath = path4.join(basePath, name); if (!fs5.existsSync(pluginPath)) await fs5.promises.mkdir(pluginPath, { recursive: true }); await Promise.all(files.map((file) => { const filePath = path4.join(pluginPath, file); if (!fs5.existsSync(filePath)) return fs5.promises.mkdir(filePath, { recursive: true }); return Promise.resolve(); })); }; getFiles = (filePath, suffixs = []) => { if (!fs5.existsSync(filePath)) { throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${filePath}`); } let files = fs5.readdirSync(filePath, { withFileTypes: true }).filter((file) => file.isFile()).map((file) => file.name); if (suffixs.length) { const normalizedSuffixs = suffixs.map((suffix) => suffix.startsWith(".") ? suffix : `.${suffix}`); files = files.filter((file) => { const suffix = path4.extname(file); return suffix && normalizedSuffixs.includes(suffix); }); } return files; }; copyFilesSync = (files, defaulPath, userPath) => { files.forEach((file) => { const defaulFile = path4.join(defaulPath, file); const userFile = path4.join(userPath, file); if (!fs5.existsSync(userFile)) { fs5.copyFileSync(defaulFile, userFile); } }); }; copyFiles = async (files, defaulPath, userPath) => { await Promise.all(files.map(async (file) => { const defaulFile = path4.join(defaulPath, file); const userFile = path4.join(userPath, file); if (!fs5.existsSync(userFile)) { await fs5.promises.copyFile(defaulFile, userFile); } })); }; copyConfigSync = (defaulPath, userPath, suffixs = [], isThrow = false) => { try { const files = getFiles(defaulPath, suffixs); fs5.mkdirSync(userPath, { recursive: true }); fs5.mkdirSync(defaulPath, { recursive: true }); copyFilesSync(files, defaulPath, userPath); return true; } catch (error) { if (isThrow) throw error; return false; } }; copyConfig = async (defaulPath, userPath, suffixs = [], isThrow = false) => { try { const files = getFiles(defaulPath, suffixs); await Promise.all([ fs5.promises.mkdir(userPath, { recursive: true }), fs5.promises.mkdir(defaulPath, { recursive: true }) ]); await copyFiles(files, defaulPath, userPath); return true; } catch (error) { if (isThrow) throw error; return false; } }; getAllFilesSync = (dir2, options = {}) => { const { suffixs = [], exclude = [], returnType = "rel" } = options; const result = []; const readDirRecursive = (currentDir, prefix = "") => { const files = fs5.readdirSync(currentDir, { withFileTypes: true }); for (const file of files) { const relativePath = path4.join(prefix, file.name); const fullPath = path4.join(currentDir, file.name); if (file.isDirectory()) { readDirRecursive(fullPath, relativePath); } else if (file.isFile()) { const suffix = path4.extname(file.name); if (suffixs.length > 0) { const normalizedSuffixs = suffixs.map((s) => s.startsWith(".") ? s : `.${s}`); if (normalizedSuffixs.includes(suffix)) { result.push(returnType === "abs" ? fullPath : relativePath); } } else if (exclude.length > 0) { const normalizedExclude = exclude.map((s) => s.startsWith(".") ? s : `.${s}`); if (!normalizedExclude.includes(suffix)) { result.push(returnType === "abs" ? fullPath : relativePath); } } else { result.push(returnType === "abs" ? fullPath : relativePath); } } } }; if (!fs5.existsSync(dir2)) { throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${dir2}`); } readDirRecursive(dir2); return result; }; getAllFiles = async (dir2, options = {}) => { const { suffixs = [], exclude = [], returnType = "rel" } = options; const result = []; const readDirRecursive = async (currentDir, prefix = "") => { const files = await fs5.promises.readdir(currentDir, { withFileTypes: true }); await Promise.all(files.map(async (file) => { const relativePath = path4.join(prefix, file.name); const fullPath = path4.join(currentDir, file.name); if (file.isDirectory()) { await readDirRecursive(fullPath, relativePath); } else if (file.isFile()) { const suffix = path4.extname(file.name); if (suffixs.length > 0) { const normalizedSuffixs = suffixs.map((s) => s.startsWith(".") ? s : `.${s}`); if (normalizedSuffixs.includes(suffix)) { result.push(returnType === "abs" ? fullPath : relativePath); } } else if (exclude.length > 0) { const normalizedExclude = exclude.map((s) => s.startsWith(".") ? s : `.${s}`); if (!normalizedExclude.includes(suffix)) { result.push(returnType === "abs" ? fullPath : relativePath); } } else { result.push(returnType === "abs" ? fullPath : relativePath); } } })); }; if (!fs5.existsSync(dir2)) { throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${dir2}`); } await readDirRecursive(dir2); return result; }; } }); var base64, buffer, stream, readFile, randomStr; var init_data = __esm({ "src/utils/fs/data.ts"() { init_file(); base64 = async (data, options = { http: false }) => { if (typeof data !== "string") { if (Buffer.isBuffer(data)) return data.toString("base64"); if (data instanceof Uint8Array) return Buffer.from(data).toString("base64"); if (data instanceof Readable) return (await stream(data)).toString("base64"); return data; } if (data.startsWith("base64://")) return data.replace("base64://", ""); if (data.startsWith("http")) { if (options.http) return data; const response = await axios10.get(data, { responseType: "stream" }); const buffer2 = await stream(response.data); return buffer2.toString("base64"); } const files = data.replace(sep, ""); if (fs5.existsSync(files)) return (await fs5.promises.readFile(files)).toString("base64"); return Buffer.from(data, "base64").toString("base64"); }; buffer = async (data, options) => { if (typeof data !== "string") { if (Buffer.isBuffer(data)) return data; if (data instanceof Uint8Array) return Buffer.from(data); if (data instanceof Readable) return await stream(data); return data; } if (data.startsWith("base64://")) { return Buffer.from(data.replace("base64://", ""), "base64"); } if (data.startsWith("http")) { if (options == null ? void 0 : options.http) return data; const response = await axios10.get(data, { responseType: "arraybuffer" }); return Buffer.from(response.data, "binary"); } const files = data.replace(sep, ""); if (fs5.existsSync(files)) return fs5.readFileSync(files); return Buffer.from(data); }; stream = (stream3) => new Promise((resolve, reject) => { const chunks = []; stream3.on("data", (chunk) => chunks.push(chunk)); stream3.on("end", () => resolve(Buffer.concat(chunks))); stream3.on("error", (error) => reject(error)); }); readFile = async (path32) => { try { const data = await fs5.promises.readFile(path32); return data; } catch (error) { logger.error(error); return null; } }; randomStr = (length = 8) => { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join(""); }; } }); var readJsonSync, writeJsonSync, readJson, writeJson, json; var init_json = __esm({ "src/utils/fs/json.ts"() { readJsonSync = (path32, isThrow = false) => { try { const data = fs5.readFileSync(path32, "utf8"); return JSON.parse(data); } catch (error) { if (isThrow) throw error; return null; } }; writeJsonSync = (path32, data, isThrow = false) => { try { fs5.writeFileSync(path32, JSON.stringify(data, null, 2)); return true; } catch (error) { if (isThrow) throw error; return false; } }; readJson = async (path32, isThrow = false) => { try { const data = await fs5.promises.readFile(path32, "utf8"); return JSON.parse(data); } catch (error) { if (isThrow) throw error; return null; } }; writeJson = async (path32, data, isThrow = false) => { try { await fs5.promises.writeFile(path32, JSON.stringify(data, null, 2)); return true; } catch (error) { if (isThrow) throw error; return false; } }; json = { /** 同步读取 */ readSync: readJsonSync, /** 同步写入 */ writeSync: writeJsonSync, /** 异步读取 */ read: readJson, /** 异步写入 */ write: writeJson }; } }); var filesByExt, splitPath, getRelPath, urlToPath, isSubPath; var init_path = __esm({ "src/utils/fs/path.ts"() { init_file(); filesByExt = (filePath, ext, returnType = "name") => { if (!fs5.existsSync(filePath) || !fs5.statSync(filePath).isDirectory()) return []; const files = fs5.readdirSync(filePath, { withFileTypes: true }); const list2 = []; if (!Array.isArray(ext)) ext = [ext]; files.forEach((v) => { if (v.isDirectory()) return; if (ext.includes(path4.extname(v.name))) { if (returnType === "name") { list2.push(v.name); } else if (returnType === "rel") { const file = path4.resolve(filePath, v.name); list2.push(path4.relative(process.cwd(), file)); } else if (returnType === "abs") { list2.push(path4.resolve(filePath, v.name)); } } }); return list2; }; splitPath = (filePath) => { const dirname = path4.dirname(filePath).replace(sep, ""); const basename = path4.basename(filePath); return { dirname, basename }; }; getRelPath = (filePath) => filePath.replace(/\\+/g, "/").replace(/\.+\/+|\/+$/g, ""); urlToPath = (url) => { const filePath = fileURLToPath$1(url); const rel = path4.relative(path4.dirname(filePath), process.cwd()); const upLevelsCount = rel.split(path4.sep).length; return lodash3.repeat("../", upLevelsCount); }; isSubPath = (root2, target, isAbs = true) => { if (isAbs) { root2 = path4.resolve(root2); target = path4.resolve(target); } const relative = path4.relative(root2, target); return relative && !relative.startsWith("..") && !path4.isAbsolute(relative); }; } }); // src/plugin/cache.ts var cache3; var init_cache = __esm({ "src/plugin/cache.ts"() { cache3 = { index: {}, accept: [], command: [], task: [], button: [], handler: {}, missing: /* @__PURE__ */ new Map(), watcher: /* @__PURE__ */ new Map(), count: { accept: 0, command: 0, task: 0, button: 0, handler: { key: 0, fnc: 0 } }, static: [] }; } }); var pkgRoot, getPluginInfo, isPlugin; var init_pkg2 = __esm({ "src/utils/fs/pkg.ts"() { init_cache(); init_require(); pkgRoot = (name, rootPath) => { let dir2; if (import.meta.url) { const require2 = createRequire(rootPath || import.meta.url); dir2 = require2.resolve(name); } else { __require.resolve(name); dir2 = __require.resolve(name); } try { if (existsSync$1(path4.join(dir2, "package.json"))) { return path4.resolve(dir2); } while (true) { dir2 = path4.dirname(dir2); if (existsSync$1(path4.join(dir2, "package.json"))) { return path4.resolve(dir2); } if (dir2 === path4.dirname(dir2)) { throw new Error(`[common] \u672A\u627E\u5230\u5305${name}\u7684\u6839\u76EE\u5F55`); } } } finally { delete __require.cache[__require.resolve(name)]; } }; getPluginInfo = (name) => { const list2 = Object.values(cache3.index); const plugin = list2.find((item) => item.name === name); if (!plugin) return null; const info = { get pkg() { if (!plugin.pkgPath) return null; return requireFileSync(plugin.pkgPath); } }; return Object.assign(plugin, info); }; isPlugin = (name) => { return !!getPluginInfo(name); }; } }); // src/utils/fs/static.ts var isPublic; var init_static = __esm({ "src/utils/fs/static.ts"() { init_path(); init_cache(); isPublic = (filePath) => { try { for (const item of cache3.static) { if (isSubPath(item, filePath)) { return true; } } return false; } catch (error) { logger.error(error); return false; } }; } }); var YamlEditor, read, write, save, comment, applyComments, yaml2; var init_yaml = __esm({ "src/utils/fs/yaml.ts"() { YamlEditor = class { filePath; doc; document; constructor(file) { this.filePath = file; const data = YAML.parseDocument(fs5.existsSync(file) ? fs5.readFileSync(file, "utf8") : file); this.doc = data; this.document = data; } /** * 获取指定路径的值 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` */ get(path32) { try { if (!path32) return this.document.toJSON(); return lodash3.get(this.document.toJSON(), path32); } catch (error) { logger.error(`[YamlEditor] \u83B7\u53D6\u6570\u636E\u65F6\u51FA\u9519\uFF1A${error}`); return null; } } /** * 设置指定路径的值 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param value - 要设置的值 允许的类型:`string`, `boolean`, `number`, `object`, `array` * @param isSplit - 是否使用分割路径路径,默认为 `true` */ set(path32, value, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; this.document.setIn(_path, value); return true; } catch (error) { logger.error(`[YamlEditor] \u8BBE\u7F6E\u6570\u636E\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 向指定路径添加新值 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param value - 要添加的值 * @param isSplit - 是否使用分割路径路径,默认为 `true` */ add(path32, value, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; this.document.addIn(_path, value); logger.debug(`[YamlEditor] \u5DF2\u5728 ${path32} \u6DFB\u52A0\u65B0\u7684\u503C`); return true; } catch (error) { logger.error(`[YamlEditor] \u6DFB\u52A0\u6570\u636E\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 删除指定路径 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param isSplit - 是否使用分割路径路径,默认为 `true` * @returns 是否删除成功 */ del(path32, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; this.document.deleteIn(_path); return true; } catch (error) { logger.error(`[YamlEditor] \u5220\u9664\u6570\u636E\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 向指定路径的数组添加新值,可以选择添加到数组的开始或结束 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param value - 要添加的值 * @param prepend - 如果为 true,则添加到数组的开头,否则添加到末尾 * @param isSplit - 是否使用分割路径路径,默认为 `true` */ append(path32, value, prepend = false, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; let current = this.document.getIn(_path); if (!current) { current = new YAML.YAMLSeq(); this.document.setIn(_path, current); } else if (!(current instanceof YAML.YAMLSeq)) { logger.error("[YamlEditor] \u6307\u5B9A\u7684\u8DEF\u5F84\u4E0D\u662F\u6570\u7EC4"); return false; } else { prepend ? current.items.unshift(value) : current.add(value); } logger.debug(`[YamlEditor] \u5DF2\u5411 ${path32} \u6570\u7EC4${prepend ? "\u5F00\u5934" : "\u672B\u5C3E"}\u6DFB\u52A0\u65B0\u5143\u7D20\uFF1A${value}`); return true; } catch (error) { logger.error(`[YamlEditor] \u5411\u6570\u7EC4\u6DFB\u52A0\u5143\u7D20\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 向指定路径的数组删除值 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param value - 要删除的值 * @param isSplit - 是否使用分割路径路径,默认为 `true` */ remove(path32, value, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; const current = this.document.getIn(_path); if (!current) { logger.error("[YamlEditor] \u6307\u5B9A\u7684\u8DEF\u5F84\u4E0D\u5B58\u5728"); return false; } if (!(current instanceof YAML.YAMLSeq)) { logger.error("[YamlEditor] \u6307\u5B9A\u7684\u8DEF\u5F84\u4E0D\u662F\u6570\u7EC4"); return false; } const index5 = current.items.findIndex((item) => lodash3.isEqual(item.toJSON(), value)); if (index5 < 0) { logger.error("[YamlEditor] \u672A\u627E\u5230\u8981\u5220\u9664\u7684\u503C"); return false; } current.items.splice(index5, 1); logger.debug(`[YamlEditor] \u5DF2\u4ECE ${path32} \u6570\u7EC4\u5220\u9664\u5143\u7D20\uFF1A${value}`); return true; } catch (error) { logger.error(`[YamlEditor] \u4ECE\u6570\u7EC4\u5220\u9664\u5143\u7D20\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 检查指定路径的键是否存在 * @param path - 路径,用点号分隔 * @param isSplit - 是否使用分割路径路径,默认为 `true` */ has(path32, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; return this.document.hasIn(_path); } catch (error) { logger.error(`[YamlEditor] \u68C0\u67E5\u8DEF\u5F84\u662F\u5426\u5B58\u5728\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 查询指定路径中是否包含指定的值 * @param path - 路径,用点号分隔 * @param value - 要查询的值 * @param isSplit - 是否使用分割路径路径,默认为 `true` */ hasval(path32, value, isSplit = true) { try { const _path = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; const current = this.document.getIn(_path); if (!current) return false; if (current instanceof YAML.YAMLSeq) { return current.items.some((item) => lodash3.isEqual(item.toJSON(), value)); } else if (current instanceof YAML.YAMLMap) { return Array.from(current.values()).some((v) => lodash3.isEqual(v.toJSON(), value)); } else { return lodash3.isEqual(current, value); } } catch (error) { logger.error(`[YamlEditor] \u68C0\u67E5\u8DEF\u5F84 ${path32} \u662F\u5426\u5305\u542B\u503C\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 查询指定路径中是否包含指定的值 * @param path - 路径,用点号分隔 * @param value - 要查询的值 * @deprecated 请使用 `hasval` 代替 */ hasVal(path32, value) { return this.hasval(path32, value); } /** * 向根节点新增元素,如果根节点不是数组,则将其转换为数组再新增元素 * @param value - 要新增的元素 */ pusharr(value) { try { if (!(this.document.contents instanceof YAML.YAMLSeq)) { this.document.contents = new YAML.YAMLSeq(); logger.debug("[YamlEditor] \u6839\u8282\u70B9\u5DF2\u8F6C\u6362\u4E3A\u6570\u7EC4"); } this.document.contents.add(value); logger.debug("[YamlEditor] \u5DF2\u5411\u6839\u8282\u70B9\u6570\u7EC4\u65B0\u589E\u5143\u7D20\uFF1A", value); return true; } catch (error) { logger.error(`[YamlEditor] \u5411\u6839\u8282\u70B9\u6570\u7EC4\u65B0\u589E\u5143\u7D20\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 根据索引从根节点数组删除元素 * @param index - 要删除元素的索引 */ delarr(index5) { try { if (!(this.document.contents instanceof YAML.YAMLSeq)) { throw new Error("[YamlEditor] \u6839\u8282\u70B9\u4E0D\u662F\u6570\u7EC4"); } if (index5 < 0 || index5 >= this.document.contents.items.length) { throw new Error("[YamlEditor] \u7D22\u5F15\u8D85\u51FA\u8303\u56F4"); } this.document.contents.items.splice(index5, 1); logger.debug("[YamlEditor] \u5DF2\u6839\u636E\u7D22\u5F15\u4ECE\u6839\u8282\u70B9\u6570\u7EC4\u5220\u9664\u5143\u7D20\uFF0C\u7D22\u5F15\uFF1A", index5); return true; } catch (error) { logger.error(`[YamlEditor] \u6839\u636E\u7D22\u5F15\u5220\u9664\u6839\u8282\u70B9\u6570\u7EC4\u5143\u7D20\u65F6\u51FA\u9519\uFF1A${error}`); return false; } } /** * 获取指定路径的pair对象 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param isSplit - 是否使用分割路径路径,默认为 `true` */ getpair(path32, isSplit = true) { if (!path32) throw new Error("path is required"); const keys = typeof path32 === "string" ? isSplit ? path32.split(".") : [path32] : path32; let pair = this.document.contents; keys.forEach((key) => { if (isMap(pair)) { pair = pair.items.find((item) => item.key.value === key); } else if (isSeq(pair)) { pair = pair.items.find((item) => item.value === key); } else if (isPair(pair)) { pair = pair.value.items.find((item) => item.key.value === key); } }); return pair; } /** * 设置指定键的注释 * @param path - 路径,多个路径使用`.`连接,例如:`a.b.c` * @param comment - 要设置的注释 * @param prepend - 如果为 true,则添加注释到开头,否则添加到同一行的末尾 * @param isSplit - 是否使用分割路径路径,默认为 `true` */ comment(path32, comment2, prepend = true, isSplit = true) { if (!path32) throw new Error("[YamlEditor] path \u4E0D\u80FD\u4E3A\u7A7A"); const pair = this.getpair(path32, isSplit); if (!pair) throw new Error(`[YamlEditor] \u672A\u627E\u5230\u8282\u70B9 ${path32}`); comment2 = ` ${comment2}`; if (prepend) { pair.key.commentBefore = comment2; } else