mioku 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,7 +27,7 @@ const node_os = __toESM(require("node:os"));
27
27
  const node_child_process = __toESM(require("node:child_process"));
28
28
 
29
29
  //#region package.json
30
- var version = "1.0.4";
30
+ var version = "1.0.6";
31
31
 
32
32
  //#endregion
33
33
  //#region src/internal/exec.ts
@@ -250,4 +250,4 @@ Object.defineProperty(exports, 'version', {
250
250
  return version;
251
251
  }
252
252
  });
253
- //# sourceMappingURL=exec-Bwnfv-TQ.cjs.map
253
+ //# sourceMappingURL=exec-O8rCedzr.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"exec-Bwnfv-TQ.cjs","names":["path","candidate: string","command: string","resolved: string | null","resolved: string","args: string[]","options: { cwd?: string; env?: NodeJS.ProcessEnv }","options: { cwd?: string }"],"sources":["../package.json","../src/internal/exec.ts"],"sourcesContent":["{\n \"name\": \"mioku\",\n \"type\": \"module\",\n \"version\": \"1.0.4\",\n \"packageManager\": \"bun@1.2.0\",\n \"description\": \"Mioku - A plugin-based QQ bot framework\",\n \"keywords\": [\n \"onebot\",\n \"framework\",\n \"bot\",\n \"mioku\"\n ],\n \"bin\": {\n \"mioku\": \"./dist/cli/index.js\"\n },\n \"engines\": {\n \"node\": \">= 22.18.0\"\n },\n \"homepage\": \"https://github.com/mioku-lab/mioku#readme\",\n \"files\": [\n \"dist\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/mioku-lab/mioku/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mioku-lab/mioku.git\",\n \"directory\": \"packages/mioku\"\n },\n \"scripts\": {\n \"dev\": \"tsdown -w\",\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"author\": \"Jerryplusy <jerryplusy@outlook.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"napcat-sdk\": \"^0.16.0\",\n \"consola\": \"^3.4.2\",\n \"dayjs\": \"^1.11.19\",\n \"dedent\": \"^1.7.1\",\n \"filesize\": \"^11.0.13\",\n \"inquirer\": \"^10.0.0\",\n \"jiti\": \"^2.6.1\",\n \"lowdb\": \"^7.0.1\",\n \"mri\": \"^1.2.0\",\n \"node-cron\": \"^4.2.1\",\n \"pretty-ms\": \"^9.3.0\",\n \"string2argv\": \"^1.0.2\",\n \"lodash\": \"^4.17.21\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.0\",\n \"@types/node\": \"^22.0.0\",\n \"tsdown\": \"^0.11.0\",\n \"typescript\": \"^5.8.0\"\n }\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nconst isWindows = process.platform === \"win32\";\n\nconst resolveCache = new Map<string, string | null>();\n\nfunction windowsExtensions(): string[] {\n const raw = process.env.PATHEXT || \".COM;.EXE;.BAT;.CMD\";\n return raw\n .split(\";\")\n .map((ext) => ext.trim())\n .filter(Boolean);\n}\n\n// PATH is a snapshot taken at process start, so a package manager installed\n// mid-run won't be on it. We enumerate every plausible install prefix for\n// bun / npm / scoop / chocolatey so the CLI can find bun in one shot.\nfunction searchDirs(): string[] {\n const raw = process.env.PATH || process.env.Path || \"\";\n const dirs = raw\n .split(path.delimiter)\n .map((d) => d.trim())\n .filter(Boolean);\n const home = os.homedir();\n const appData = process.env.APPDATA || path.join(home, \"AppData\", \"Roaming\");\n const localAppData =\n process.env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\");\n const programFiles = process.env.ProgramFiles || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"ProgramFiles(x86)\"] || \"C:\\\\Program Files (x86)\";\n\n const extra = isWindows\n ? [\n // 官方安装器\n path.join(home, \".bun\", \"bin\"),\n // npm 全局(带 .cmd shim)\n path.join(appData, \"npm\"),\n path.join(appData, \"npm\", \"node_modules\", \".bin\"),\n // 备用 bun 位置\n path.join(localAppData, \"bun\", \"bin\"),\n path.join(programFiles, \"bun\", \"bin\"),\n path.join(programFilesX86, \"bun\", \"bin\"),\n // scoop\n path.join(home, \"scoop\", \"shims\"),\n // chocolatey\n \"C:\\\\ProgramData\\\\chocolatey\\\\bin\",\n // node 自带(很多用户 npm i -g 装到 node 目录)\n path.join(programFiles, \"nodejs\"),\n path.join(programFilesX86, \"nodejs\"),\n ]\n : [\n path.join(home, \".bun\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n path.join(home, \".npm-global\", \"bin\"),\n ];\n\n return [...dirs, ...extra.filter((dir) => dir && !dir.endsWith(path.sep))];\n}\n\nfunction isExecutableFile(candidate: string): boolean {\n try {\n return fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n}\n\nfunction searchCommandViaShell(command: string): string | null {\n if (command.includes(\"/\") || command.includes(path.sep)) return null;\n const shellCmd = isWindows ? \"where\" : \"which\";\n try {\n const result = spawnSync(shellCmd, [command], {\n encoding: \"utf-8\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: 5000,\n });\n if (result.status === 0 && result.stdout) {\n const first = result.stdout\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .find(Boolean);\n if (first && isExecutableFile(first)) return first;\n }\n } catch {}\n return null;\n}\n\n/** 在 PATH 及常见安装目录中解析命令的完整路径,结果带缓存 */\nexport function resolveCommand(command: string): string | null {\n if (resolveCache.has(command)) return resolveCache.get(command) ?? null;\n\n let resolved: string | null = null;\n\n if (command.includes(\"/\") || command.includes(path.sep)) {\n resolved = isExecutableFile(command) ? path.resolve(command) : null;\n } else {\n const exts = isWindows ? windowsExtensions() : [\"\"];\n const hasKnownExt =\n isWindows &&\n exts.some((ext) => command.toLowerCase().endsWith(ext.toLowerCase()));\n\n outer: for (const dir of searchDirs()) {\n if (hasKnownExt) {\n const direct = path.join(dir, command);\n if (isExecutableFile(direct)) {\n resolved = direct;\n break;\n }\n continue;\n }\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isExecutableFile(candidate)) {\n resolved = candidate;\n break outer;\n }\n }\n }\n\n if (!resolved) resolved = searchCommandViaShell(command);\n }\n\n resolveCache.set(command, resolved);\n return resolved;\n}\n\nexport function clearCommandCache(): void {\n resolveCache.clear();\n}\n\nfunction needsCmdShell(resolved: string): boolean {\n if (!isWindows) return false;\n const ext = path.extname(resolved).toLowerCase();\n return ext === \".cmd\" || ext === \".bat\";\n}\n\nexport interface SpawnPlan {\n file: string;\n args: string[];\n windowsVerbatimArguments?: boolean;\n shell?: boolean | string;\n}\n\n// Windows can't CreateProcess a .cmd/.bat shim directly (npm installs bun as\n// bun.cmd), so those get routed through cmd.exe via shell: true. Previously\n// we hand-rolled a cmd.exe invocation with windowsVerbatimArguments: true,\n// but that combination misbehaves with spawnSync + stdio:\"inherit\" on\n// Node.js >= 20 (manifests as the spawned process printing \"undefined\" or\n// a spurious EINVAL), so we let Node's shell wrapper do the quoting.\n/** 生成跨平台的 spawn 参数,Windows 下 .cmd/.bat 会改经 cmd.exe 执行 */\nexport function buildSpawnPlan(command: string, args: string[]): SpawnPlan {\n const resolved = resolveCommand(command);\n\n if (!resolved) {\n return { file: command, args };\n }\n\n if (!needsCmdShell(resolved)) {\n return { file: resolved, args };\n }\n\n return {\n file: resolved,\n args,\n shell: true,\n };\n}\n\nexport interface RunCommandResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/** 执行命令并收集 stdout/stderr 与退出码,失败不抛异常 */\nexport function runCommand(\n command: string,\n args: string[],\n options: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<RunCommandResult> {\n const plan = buildSpawnPlan(command, args);\n\n return new Promise((resolve) => {\n const child = spawn(plan.file, plan.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: plan.shell,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, code: code ?? 1 });\n });\n child.on(\"error\", (error) => {\n resolve({\n stdout,\n stderr: `${stderr}\\n${error.message}`.trim(),\n code: 1,\n });\n });\n });\n}\n\n/** 同步执行命令并继承当前终端的输入输出,非零退出码时抛错 */\nexport function runCommandInherit(\n command: string,\n args: string[],\n options: { cwd?: string } = {},\n): void {\n const plan = buildSpawnPlan(command, args);\n const result = spawnSync(plan.file, plan.args, {\n cwd: options.cwd,\n stdio: \"inherit\",\n shell: plan.shell,\n });\n\n if (result.error) throw result.error;\n if (result.status !== 0) {\n throw new Error(`${command} 退出码 ${result.status}`);\n }\n}\n\n/** 判断命令是否可用 */\nexport function commandExists(command: string): boolean {\n return resolveCommand(command) !== null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"exec-O8rCedzr.cjs","names":["path","candidate: string","command: string","resolved: string | null","resolved: string","args: string[]","options: { cwd?: string; env?: NodeJS.ProcessEnv }","options: { cwd?: string }"],"sources":["../package.json","../src/internal/exec.ts"],"sourcesContent":["{\n \"name\": \"mioku\",\n \"type\": \"module\",\n \"version\": \"1.0.6\",\n \"packageManager\": \"bun@1.2.0\",\n \"description\": \"Mioku - A plugin-based QQ bot framework\",\n \"keywords\": [\n \"onebot\",\n \"framework\",\n \"bot\",\n \"mioku\"\n ],\n \"bin\": {\n \"mioku\": \"./dist/cli/index.js\"\n },\n \"engines\": {\n \"node\": \">= 22.18.0\"\n },\n \"homepage\": \"https://github.com/mioku-lab/mioku#readme\",\n \"files\": [\n \"dist\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/mioku-lab/mioku/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mioku-lab/mioku.git\",\n \"directory\": \"packages/mioku\"\n },\n \"scripts\": {\n \"dev\": \"tsdown -w\",\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"author\": \"Jerryplusy <jerryplusy@outlook.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"napcat-sdk\": \"^0.16.0\",\n \"consola\": \"^3.4.2\",\n \"dayjs\": \"^1.11.19\",\n \"dedent\": \"^1.7.1\",\n \"filesize\": \"^11.0.13\",\n \"inquirer\": \"^10.0.0\",\n \"jiti\": \"^2.6.1\",\n \"lowdb\": \"^7.0.1\",\n \"mri\": \"^1.2.0\",\n \"node-cron\": \"^4.2.1\",\n \"pretty-ms\": \"^9.3.0\",\n \"string2argv\": \"^1.0.2\",\n \"lodash\": \"^4.17.21\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.0\",\n \"@types/node\": \"^22.0.0\",\n \"tsdown\": \"^0.11.0\",\n \"typescript\": \"^5.8.0\"\n }\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nconst isWindows = process.platform === \"win32\";\n\nconst resolveCache = new Map<string, string | null>();\n\nfunction windowsExtensions(): string[] {\n const raw = process.env.PATHEXT || \".COM;.EXE;.BAT;.CMD\";\n return raw\n .split(\";\")\n .map((ext) => ext.trim())\n .filter(Boolean);\n}\n\n// PATH is a snapshot taken at process start, so a package manager installed\n// mid-run won't be on it. We enumerate every plausible install prefix for\n// bun / npm / scoop / chocolatey so the CLI can find bun in one shot.\nfunction searchDirs(): string[] {\n const raw = process.env.PATH || process.env.Path || \"\";\n const dirs = raw\n .split(path.delimiter)\n .map((d) => d.trim())\n .filter(Boolean);\n const home = os.homedir();\n const appData = process.env.APPDATA || path.join(home, \"AppData\", \"Roaming\");\n const localAppData =\n process.env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\");\n const programFiles = process.env.ProgramFiles || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"ProgramFiles(x86)\"] || \"C:\\\\Program Files (x86)\";\n\n const extra = isWindows\n ? [\n // 官方安装器\n path.join(home, \".bun\", \"bin\"),\n // npm 全局(带 .cmd shim)\n path.join(appData, \"npm\"),\n path.join(appData, \"npm\", \"node_modules\", \".bin\"),\n // 备用 bun 位置\n path.join(localAppData, \"bun\", \"bin\"),\n path.join(programFiles, \"bun\", \"bin\"),\n path.join(programFilesX86, \"bun\", \"bin\"),\n // scoop\n path.join(home, \"scoop\", \"shims\"),\n // chocolatey\n \"C:\\\\ProgramData\\\\chocolatey\\\\bin\",\n // node 自带(很多用户 npm i -g 装到 node 目录)\n path.join(programFiles, \"nodejs\"),\n path.join(programFilesX86, \"nodejs\"),\n ]\n : [\n path.join(home, \".bun\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n path.join(home, \".npm-global\", \"bin\"),\n ];\n\n return [...dirs, ...extra.filter((dir) => dir && !dir.endsWith(path.sep))];\n}\n\nfunction isExecutableFile(candidate: string): boolean {\n try {\n return fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n}\n\nfunction searchCommandViaShell(command: string): string | null {\n if (command.includes(\"/\") || command.includes(path.sep)) return null;\n const shellCmd = isWindows ? \"where\" : \"which\";\n try {\n const result = spawnSync(shellCmd, [command], {\n encoding: \"utf-8\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: 5000,\n });\n if (result.status === 0 && result.stdout) {\n const first = result.stdout\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .find(Boolean);\n if (first && isExecutableFile(first)) return first;\n }\n } catch {}\n return null;\n}\n\n/** 在 PATH 及常见安装目录中解析命令的完整路径,结果带缓存 */\nexport function resolveCommand(command: string): string | null {\n if (resolveCache.has(command)) return resolveCache.get(command) ?? null;\n\n let resolved: string | null = null;\n\n if (command.includes(\"/\") || command.includes(path.sep)) {\n resolved = isExecutableFile(command) ? path.resolve(command) : null;\n } else {\n const exts = isWindows ? windowsExtensions() : [\"\"];\n const hasKnownExt =\n isWindows &&\n exts.some((ext) => command.toLowerCase().endsWith(ext.toLowerCase()));\n\n outer: for (const dir of searchDirs()) {\n if (hasKnownExt) {\n const direct = path.join(dir, command);\n if (isExecutableFile(direct)) {\n resolved = direct;\n break;\n }\n continue;\n }\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isExecutableFile(candidate)) {\n resolved = candidate;\n break outer;\n }\n }\n }\n\n if (!resolved) resolved = searchCommandViaShell(command);\n }\n\n resolveCache.set(command, resolved);\n return resolved;\n}\n\nexport function clearCommandCache(): void {\n resolveCache.clear();\n}\n\nfunction needsCmdShell(resolved: string): boolean {\n if (!isWindows) return false;\n const ext = path.extname(resolved).toLowerCase();\n return ext === \".cmd\" || ext === \".bat\";\n}\n\nexport interface SpawnPlan {\n file: string;\n args: string[];\n windowsVerbatimArguments?: boolean;\n shell?: boolean | string;\n}\n\n// Windows can't CreateProcess a .cmd/.bat shim directly (npm installs bun as\n// bun.cmd), so those get routed through cmd.exe via shell: true. Previously\n// we hand-rolled a cmd.exe invocation with windowsVerbatimArguments: true,\n// but that combination misbehaves with spawnSync + stdio:\"inherit\" on\n// Node.js >= 20 (manifests as the spawned process printing \"undefined\" or\n// a spurious EINVAL), so we let Node's shell wrapper do the quoting.\n/** 生成跨平台的 spawn 参数,Windows 下 .cmd/.bat 会改经 cmd.exe 执行 */\nexport function buildSpawnPlan(command: string, args: string[]): SpawnPlan {\n const resolved = resolveCommand(command);\n\n if (!resolved) {\n return { file: command, args };\n }\n\n if (!needsCmdShell(resolved)) {\n return { file: resolved, args };\n }\n\n return {\n file: resolved,\n args,\n shell: true,\n };\n}\n\nexport interface RunCommandResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/** 执行命令并收集 stdout/stderr 与退出码,失败不抛异常 */\nexport function runCommand(\n command: string,\n args: string[],\n options: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<RunCommandResult> {\n const plan = buildSpawnPlan(command, args);\n\n return new Promise((resolve) => {\n const child = spawn(plan.file, plan.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: plan.shell,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, code: code ?? 1 });\n });\n child.on(\"error\", (error) => {\n resolve({\n stdout,\n stderr: `${stderr}\\n${error.message}`.trim(),\n code: 1,\n });\n });\n });\n}\n\n/** 同步执行命令并继承当前终端的输入输出,非零退出码时抛错 */\nexport function runCommandInherit(\n command: string,\n args: string[],\n options: { cwd?: string } = {},\n): void {\n const plan = buildSpawnPlan(command, args);\n const result = spawnSync(plan.file, plan.args, {\n cwd: options.cwd,\n stdio: \"inherit\",\n shell: plan.shell,\n });\n\n if (result.error) throw result.error;\n if (result.status !== 0) {\n throw new Error(`${command} 退出码 ${result.status}`);\n }\n}\n\n/** 判断命令是否可用 */\nexport function commandExists(command: string): boolean {\n return resolveCommand(command) !== null;\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import * as os$1 from "node:os";
4
4
  import { spawn, spawnSync } from "node:child_process";
5
5
 
6
6
  //#region package.json
7
- var version = "1.0.4";
7
+ var version = "1.0.6";
8
8
 
9
9
  //#endregion
10
10
  //#region src/internal/exec.ts
@@ -180,4 +180,4 @@ function commandExists(command) {
180
180
 
181
181
  //#endregion
182
182
  export { buildSpawnPlan, clearCommandCache, commandExists, resolveCommand, runCommand, runCommandInherit, version };
183
- //# sourceMappingURL=exec-B6AHNNhG.js.map
183
+ //# sourceMappingURL=exec-p6Vp158F.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"exec-B6AHNNhG.js","names":["path","candidate: string","command: string","resolved: string | null","resolved: string","args: string[]","options: { cwd?: string; env?: NodeJS.ProcessEnv }","options: { cwd?: string }"],"sources":["../package.json","../src/internal/exec.ts"],"sourcesContent":["{\n \"name\": \"mioku\",\n \"type\": \"module\",\n \"version\": \"1.0.4\",\n \"packageManager\": \"bun@1.2.0\",\n \"description\": \"Mioku - A plugin-based QQ bot framework\",\n \"keywords\": [\n \"onebot\",\n \"framework\",\n \"bot\",\n \"mioku\"\n ],\n \"bin\": {\n \"mioku\": \"./dist/cli/index.js\"\n },\n \"engines\": {\n \"node\": \">= 22.18.0\"\n },\n \"homepage\": \"https://github.com/mioku-lab/mioku#readme\",\n \"files\": [\n \"dist\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/mioku-lab/mioku/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mioku-lab/mioku.git\",\n \"directory\": \"packages/mioku\"\n },\n \"scripts\": {\n \"dev\": \"tsdown -w\",\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"author\": \"Jerryplusy <jerryplusy@outlook.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"napcat-sdk\": \"^0.16.0\",\n \"consola\": \"^3.4.2\",\n \"dayjs\": \"^1.11.19\",\n \"dedent\": \"^1.7.1\",\n \"filesize\": \"^11.0.13\",\n \"inquirer\": \"^10.0.0\",\n \"jiti\": \"^2.6.1\",\n \"lowdb\": \"^7.0.1\",\n \"mri\": \"^1.2.0\",\n \"node-cron\": \"^4.2.1\",\n \"pretty-ms\": \"^9.3.0\",\n \"string2argv\": \"^1.0.2\",\n \"lodash\": \"^4.17.21\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.0\",\n \"@types/node\": \"^22.0.0\",\n \"tsdown\": \"^0.11.0\",\n \"typescript\": \"^5.8.0\"\n }\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nconst isWindows = process.platform === \"win32\";\n\nconst resolveCache = new Map<string, string | null>();\n\nfunction windowsExtensions(): string[] {\n const raw = process.env.PATHEXT || \".COM;.EXE;.BAT;.CMD\";\n return raw\n .split(\";\")\n .map((ext) => ext.trim())\n .filter(Boolean);\n}\n\n// PATH is a snapshot taken at process start, so a package manager installed\n// mid-run won't be on it. We enumerate every plausible install prefix for\n// bun / npm / scoop / chocolatey so the CLI can find bun in one shot.\nfunction searchDirs(): string[] {\n const raw = process.env.PATH || process.env.Path || \"\";\n const dirs = raw\n .split(path.delimiter)\n .map((d) => d.trim())\n .filter(Boolean);\n const home = os.homedir();\n const appData = process.env.APPDATA || path.join(home, \"AppData\", \"Roaming\");\n const localAppData =\n process.env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\");\n const programFiles = process.env.ProgramFiles || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"ProgramFiles(x86)\"] || \"C:\\\\Program Files (x86)\";\n\n const extra = isWindows\n ? [\n // 官方安装器\n path.join(home, \".bun\", \"bin\"),\n // npm 全局(带 .cmd shim)\n path.join(appData, \"npm\"),\n path.join(appData, \"npm\", \"node_modules\", \".bin\"),\n // 备用 bun 位置\n path.join(localAppData, \"bun\", \"bin\"),\n path.join(programFiles, \"bun\", \"bin\"),\n path.join(programFilesX86, \"bun\", \"bin\"),\n // scoop\n path.join(home, \"scoop\", \"shims\"),\n // chocolatey\n \"C:\\\\ProgramData\\\\chocolatey\\\\bin\",\n // node 自带(很多用户 npm i -g 装到 node 目录)\n path.join(programFiles, \"nodejs\"),\n path.join(programFilesX86, \"nodejs\"),\n ]\n : [\n path.join(home, \".bun\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n path.join(home, \".npm-global\", \"bin\"),\n ];\n\n return [...dirs, ...extra.filter((dir) => dir && !dir.endsWith(path.sep))];\n}\n\nfunction isExecutableFile(candidate: string): boolean {\n try {\n return fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n}\n\nfunction searchCommandViaShell(command: string): string | null {\n if (command.includes(\"/\") || command.includes(path.sep)) return null;\n const shellCmd = isWindows ? \"where\" : \"which\";\n try {\n const result = spawnSync(shellCmd, [command], {\n encoding: \"utf-8\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: 5000,\n });\n if (result.status === 0 && result.stdout) {\n const first = result.stdout\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .find(Boolean);\n if (first && isExecutableFile(first)) return first;\n }\n } catch {}\n return null;\n}\n\n/** 在 PATH 及常见安装目录中解析命令的完整路径,结果带缓存 */\nexport function resolveCommand(command: string): string | null {\n if (resolveCache.has(command)) return resolveCache.get(command) ?? null;\n\n let resolved: string | null = null;\n\n if (command.includes(\"/\") || command.includes(path.sep)) {\n resolved = isExecutableFile(command) ? path.resolve(command) : null;\n } else {\n const exts = isWindows ? windowsExtensions() : [\"\"];\n const hasKnownExt =\n isWindows &&\n exts.some((ext) => command.toLowerCase().endsWith(ext.toLowerCase()));\n\n outer: for (const dir of searchDirs()) {\n if (hasKnownExt) {\n const direct = path.join(dir, command);\n if (isExecutableFile(direct)) {\n resolved = direct;\n break;\n }\n continue;\n }\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isExecutableFile(candidate)) {\n resolved = candidate;\n break outer;\n }\n }\n }\n\n if (!resolved) resolved = searchCommandViaShell(command);\n }\n\n resolveCache.set(command, resolved);\n return resolved;\n}\n\nexport function clearCommandCache(): void {\n resolveCache.clear();\n}\n\nfunction needsCmdShell(resolved: string): boolean {\n if (!isWindows) return false;\n const ext = path.extname(resolved).toLowerCase();\n return ext === \".cmd\" || ext === \".bat\";\n}\n\nexport interface SpawnPlan {\n file: string;\n args: string[];\n windowsVerbatimArguments?: boolean;\n shell?: boolean | string;\n}\n\n// Windows can't CreateProcess a .cmd/.bat shim directly (npm installs bun as\n// bun.cmd), so those get routed through cmd.exe via shell: true. Previously\n// we hand-rolled a cmd.exe invocation with windowsVerbatimArguments: true,\n// but that combination misbehaves with spawnSync + stdio:\"inherit\" on\n// Node.js >= 20 (manifests as the spawned process printing \"undefined\" or\n// a spurious EINVAL), so we let Node's shell wrapper do the quoting.\n/** 生成跨平台的 spawn 参数,Windows 下 .cmd/.bat 会改经 cmd.exe 执行 */\nexport function buildSpawnPlan(command: string, args: string[]): SpawnPlan {\n const resolved = resolveCommand(command);\n\n if (!resolved) {\n return { file: command, args };\n }\n\n if (!needsCmdShell(resolved)) {\n return { file: resolved, args };\n }\n\n return {\n file: resolved,\n args,\n shell: true,\n };\n}\n\nexport interface RunCommandResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/** 执行命令并收集 stdout/stderr 与退出码,失败不抛异常 */\nexport function runCommand(\n command: string,\n args: string[],\n options: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<RunCommandResult> {\n const plan = buildSpawnPlan(command, args);\n\n return new Promise((resolve) => {\n const child = spawn(plan.file, plan.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: plan.shell,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, code: code ?? 1 });\n });\n child.on(\"error\", (error) => {\n resolve({\n stdout,\n stderr: `${stderr}\\n${error.message}`.trim(),\n code: 1,\n });\n });\n });\n}\n\n/** 同步执行命令并继承当前终端的输入输出,非零退出码时抛错 */\nexport function runCommandInherit(\n command: string,\n args: string[],\n options: { cwd?: string } = {},\n): void {\n const plan = buildSpawnPlan(command, args);\n const result = spawnSync(plan.file, plan.args, {\n cwd: options.cwd,\n stdio: \"inherit\",\n shell: plan.shell,\n });\n\n if (result.error) throw result.error;\n if (result.status !== 0) {\n throw new Error(`${command} 退出码 ${result.status}`);\n }\n}\n\n/** 判断命令是否可用 */\nexport function commandExists(command: string): boolean {\n return resolveCommand(command) !== null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"exec-p6Vp158F.js","names":["path","candidate: string","command: string","resolved: string | null","resolved: string","args: string[]","options: { cwd?: string; env?: NodeJS.ProcessEnv }","options: { cwd?: string }"],"sources":["../package.json","../src/internal/exec.ts"],"sourcesContent":["{\n \"name\": \"mioku\",\n \"type\": \"module\",\n \"version\": \"1.0.6\",\n \"packageManager\": \"bun@1.2.0\",\n \"description\": \"Mioku - A plugin-based QQ bot framework\",\n \"keywords\": [\n \"onebot\",\n \"framework\",\n \"bot\",\n \"mioku\"\n ],\n \"bin\": {\n \"mioku\": \"./dist/cli/index.js\"\n },\n \"engines\": {\n \"node\": \">= 22.18.0\"\n },\n \"homepage\": \"https://github.com/mioku-lab/mioku#readme\",\n \"files\": [\n \"dist\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/mioku-lab/mioku/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mioku-lab/mioku.git\",\n \"directory\": \"packages/mioku\"\n },\n \"scripts\": {\n \"dev\": \"tsdown -w\",\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"author\": \"Jerryplusy <jerryplusy@outlook.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"napcat-sdk\": \"^0.16.0\",\n \"consola\": \"^3.4.2\",\n \"dayjs\": \"^1.11.19\",\n \"dedent\": \"^1.7.1\",\n \"filesize\": \"^11.0.13\",\n \"inquirer\": \"^10.0.0\",\n \"jiti\": \"^2.6.1\",\n \"lowdb\": \"^7.0.1\",\n \"mri\": \"^1.2.0\",\n \"node-cron\": \"^4.2.1\",\n \"pretty-ms\": \"^9.3.0\",\n \"string2argv\": \"^1.0.2\",\n \"lodash\": \"^4.17.21\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.0\",\n \"@types/node\": \"^22.0.0\",\n \"tsdown\": \"^0.11.0\",\n \"typescript\": \"^5.8.0\"\n }\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nconst isWindows = process.platform === \"win32\";\n\nconst resolveCache = new Map<string, string | null>();\n\nfunction windowsExtensions(): string[] {\n const raw = process.env.PATHEXT || \".COM;.EXE;.BAT;.CMD\";\n return raw\n .split(\";\")\n .map((ext) => ext.trim())\n .filter(Boolean);\n}\n\n// PATH is a snapshot taken at process start, so a package manager installed\n// mid-run won't be on it. We enumerate every plausible install prefix for\n// bun / npm / scoop / chocolatey so the CLI can find bun in one shot.\nfunction searchDirs(): string[] {\n const raw = process.env.PATH || process.env.Path || \"\";\n const dirs = raw\n .split(path.delimiter)\n .map((d) => d.trim())\n .filter(Boolean);\n const home = os.homedir();\n const appData = process.env.APPDATA || path.join(home, \"AppData\", \"Roaming\");\n const localAppData =\n process.env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\");\n const programFiles = process.env.ProgramFiles || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"ProgramFiles(x86)\"] || \"C:\\\\Program Files (x86)\";\n\n const extra = isWindows\n ? [\n // 官方安装器\n path.join(home, \".bun\", \"bin\"),\n // npm 全局(带 .cmd shim)\n path.join(appData, \"npm\"),\n path.join(appData, \"npm\", \"node_modules\", \".bin\"),\n // 备用 bun 位置\n path.join(localAppData, \"bun\", \"bin\"),\n path.join(programFiles, \"bun\", \"bin\"),\n path.join(programFilesX86, \"bun\", \"bin\"),\n // scoop\n path.join(home, \"scoop\", \"shims\"),\n // chocolatey\n \"C:\\\\ProgramData\\\\chocolatey\\\\bin\",\n // node 自带(很多用户 npm i -g 装到 node 目录)\n path.join(programFiles, \"nodejs\"),\n path.join(programFilesX86, \"nodejs\"),\n ]\n : [\n path.join(home, \".bun\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n path.join(home, \".npm-global\", \"bin\"),\n ];\n\n return [...dirs, ...extra.filter((dir) => dir && !dir.endsWith(path.sep))];\n}\n\nfunction isExecutableFile(candidate: string): boolean {\n try {\n return fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n}\n\nfunction searchCommandViaShell(command: string): string | null {\n if (command.includes(\"/\") || command.includes(path.sep)) return null;\n const shellCmd = isWindows ? \"where\" : \"which\";\n try {\n const result = spawnSync(shellCmd, [command], {\n encoding: \"utf-8\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: 5000,\n });\n if (result.status === 0 && result.stdout) {\n const first = result.stdout\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .find(Boolean);\n if (first && isExecutableFile(first)) return first;\n }\n } catch {}\n return null;\n}\n\n/** 在 PATH 及常见安装目录中解析命令的完整路径,结果带缓存 */\nexport function resolveCommand(command: string): string | null {\n if (resolveCache.has(command)) return resolveCache.get(command) ?? null;\n\n let resolved: string | null = null;\n\n if (command.includes(\"/\") || command.includes(path.sep)) {\n resolved = isExecutableFile(command) ? path.resolve(command) : null;\n } else {\n const exts = isWindows ? windowsExtensions() : [\"\"];\n const hasKnownExt =\n isWindows &&\n exts.some((ext) => command.toLowerCase().endsWith(ext.toLowerCase()));\n\n outer: for (const dir of searchDirs()) {\n if (hasKnownExt) {\n const direct = path.join(dir, command);\n if (isExecutableFile(direct)) {\n resolved = direct;\n break;\n }\n continue;\n }\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isExecutableFile(candidate)) {\n resolved = candidate;\n break outer;\n }\n }\n }\n\n if (!resolved) resolved = searchCommandViaShell(command);\n }\n\n resolveCache.set(command, resolved);\n return resolved;\n}\n\nexport function clearCommandCache(): void {\n resolveCache.clear();\n}\n\nfunction needsCmdShell(resolved: string): boolean {\n if (!isWindows) return false;\n const ext = path.extname(resolved).toLowerCase();\n return ext === \".cmd\" || ext === \".bat\";\n}\n\nexport interface SpawnPlan {\n file: string;\n args: string[];\n windowsVerbatimArguments?: boolean;\n shell?: boolean | string;\n}\n\n// Windows can't CreateProcess a .cmd/.bat shim directly (npm installs bun as\n// bun.cmd), so those get routed through cmd.exe via shell: true. Previously\n// we hand-rolled a cmd.exe invocation with windowsVerbatimArguments: true,\n// but that combination misbehaves with spawnSync + stdio:\"inherit\" on\n// Node.js >= 20 (manifests as the spawned process printing \"undefined\" or\n// a spurious EINVAL), so we let Node's shell wrapper do the quoting.\n/** 生成跨平台的 spawn 参数,Windows 下 .cmd/.bat 会改经 cmd.exe 执行 */\nexport function buildSpawnPlan(command: string, args: string[]): SpawnPlan {\n const resolved = resolveCommand(command);\n\n if (!resolved) {\n return { file: command, args };\n }\n\n if (!needsCmdShell(resolved)) {\n return { file: resolved, args };\n }\n\n return {\n file: resolved,\n args,\n shell: true,\n };\n}\n\nexport interface RunCommandResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/** 执行命令并收集 stdout/stderr 与退出码,失败不抛异常 */\nexport function runCommand(\n command: string,\n args: string[],\n options: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<RunCommandResult> {\n const plan = buildSpawnPlan(command, args);\n\n return new Promise((resolve) => {\n const child = spawn(plan.file, plan.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: plan.shell,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, code: code ?? 1 });\n });\n child.on(\"error\", (error) => {\n resolve({\n stdout,\n stderr: `${stderr}\\n${error.message}`.trim(),\n code: 1,\n });\n });\n });\n}\n\n/** 同步执行命令并继承当前终端的输入输出,非零退出码时抛错 */\nexport function runCommandInherit(\n command: string,\n args: string[],\n options: { cwd?: string } = {},\n): void {\n const plan = buildSpawnPlan(command, args);\n const result = spawnSync(plan.file, plan.args, {\n cwd: options.cwd,\n stdio: \"inherit\",\n shell: plan.shell,\n });\n\n if (result.error) throw result.error;\n if (result.status !== 0) {\n throw new Error(`${command} 退出码 ${result.status}`);\n }\n}\n\n/** 判断命令是否可用 */\nexport function commandExists(command: string): boolean {\n return resolveCommand(command) !== null;\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_exec = require('./exec-Bwnfv-TQ.cjs');
1
+ const require_exec = require('./exec-O8rCedzr.cjs');
2
2
  const node_fs = require_exec.__toESM(require("node:fs"));
3
3
  const node_path = require_exec.__toESM(require("node:path"));
4
4
  const node_url = require_exec.__toESM(require("node:url"));
@@ -677,6 +677,8 @@ const conversationGetHistory = defineCapability("conversation.gethistory", 1);
677
677
  const profileSet = defineCapability("profile.set", 1);
678
678
  /** 设置 bot 头像 */
679
679
  const avatarSet = defineCapability("profile.setavatar", 1);
680
+ /** 获取 bot 头像,返回头像地址;平台无法提供时返回 null */
681
+ const avatarGet = defineCapability("profile.getavatar", 1);
680
682
 
681
683
  //#endregion
682
684
  //#region src/capabilities/system.ts
@@ -782,6 +784,7 @@ const bindCapabilities = (bot, registry) => {
782
784
  bindDefault(bot, "deleteFriend", (user_id) => call(friendDelete, { user_id: toId(user_id) }));
783
785
  bindDefault(bot, "setProfile", (profile) => call(profileSet, profile));
784
786
  bindDefault(bot, "setAvatar", (file) => call(avatarSet, { file }));
787
+ bindDefault(bot, "getAvatar", () => call(avatarGet, {}));
785
788
  bindDefault(bot, "getHistory", (t, before, limit, extra) => call(conversationGetHistory, {
786
789
  target: t,
787
790
  before,
@@ -853,7 +856,6 @@ var AdapterContextImpl = class {
853
856
  #logger;
854
857
  #emit;
855
858
  #pendingStarts = new Set();
856
- #crossDedup;
857
859
  constructor(options) {
858
860
  this.#state = options.state;
859
861
  this.#bots = options.bots;
@@ -862,7 +864,6 @@ var AdapterContextImpl = class {
862
864
  this.#capabilities = options.capabilities;
863
865
  this.#logger = options.logger;
864
866
  this.#emit = options.emit;
865
- this.#crossDedup = options.crossDedup ?? null;
866
867
  }
867
868
  registerBot(bot) {
868
869
  return this.#bots.register(bot);
@@ -935,10 +936,6 @@ var AdapterContextImpl = class {
935
936
  return;
936
937
  }
937
938
  }
938
- if (this.#crossDedup?.isDuplicate(event)) {
939
- this.#logger.debug(`丢弃跨适配器重复消息: adapter=${event.identity?.adapter ?? ""} sender=${event.user_id ?? ""}`);
940
- return;
941
- }
942
939
  }
943
940
  await this.#bus.dispatch(event);
944
941
  }
@@ -1891,6 +1888,104 @@ const Services = {
1891
1888
  Help: defineService("help")
1892
1889
  };
1893
1890
 
1891
+ //#endregion
1892
+ //#region src/runtime/cross-adapter-dedup.ts
1893
+ const PRUNE_INTERVAL = 128;
1894
+ const MESSAGE_TTL_MS = 15e3;
1895
+ const EVENT_TTL_MS = 6e4;
1896
+ const MAX_SIZE = 4096;
1897
+ const MAX_SEGMENTS = 16;
1898
+ const MAX_TEXT = 256;
1899
+ const contentFingerprintOf = (event) => {
1900
+ const parts = [];
1901
+ for (const seg of event.message) {
1902
+ if (parts.length >= MAX_SEGMENTS) break;
1903
+ if (seg.type === "reply") continue;
1904
+ const data = seg.data ?? {};
1905
+ if (seg.type === "text") {
1906
+ const text$1 = typeof data.text === "string" ? data.text : "";
1907
+ parts.push(`t:${text$1.slice(0, MAX_TEXT)}`);
1908
+ } else if (seg.type === "at") {
1909
+ const target = data.qq ?? data.target;
1910
+ parts.push(`a:${target == null ? "" : String(target)}`);
1911
+ } else if (seg.type === "face") parts.push("f");
1912
+ else parts.push(seg.type);
1913
+ }
1914
+ return parts.join("|");
1915
+ };
1916
+ const scopeKeyOf = (event, content) => [
1917
+ event.message_type ?? "",
1918
+ event.sender?.nickname?.trim() || event.user_id || "",
1919
+ content
1920
+ ].join("|");
1921
+ const noticeKeyOf = (event) => [
1922
+ event.kind,
1923
+ event.identity.event_type,
1924
+ event.notice_type ?? "",
1925
+ event.sub_type ?? "",
1926
+ event.group_id ?? "",
1927
+ event.user_id ?? "",
1928
+ event.operator_id ?? "",
1929
+ event.identity.fingerprint ?? "",
1930
+ event.identity.timestamp == null ? "" : Math.floor(event.identity.timestamp / 1e3)
1931
+ ].join("|");
1932
+ const requestKeyOf = (event) => [
1933
+ event.kind,
1934
+ event.identity.event_type,
1935
+ event.request_type ?? "",
1936
+ event.sub_type ?? "",
1937
+ event.group_id ?? "",
1938
+ event.user_id ?? "",
1939
+ event.comment ?? "",
1940
+ event.identity.fingerprint ?? "",
1941
+ event.identity.timestamp == null ? "" : Math.floor(event.identity.timestamp / 1e3)
1942
+ ].join("|");
1943
+ /**
1944
+ * 跨适配器消息去重(L2)
1945
+ */
1946
+ var CrossAdapterEventDeduplicator = class {
1947
+ #entries = new Map();
1948
+ #inserts = 0;
1949
+ #dropped = 0;
1950
+ /** 本次运行以来被丢弃的跨适配器重复消息数 */
1951
+ get dropped() {
1952
+ return this.#dropped;
1953
+ }
1954
+ isDuplicate(event) {
1955
+ let key;
1956
+ let ttl;
1957
+ if (event.kind === "message") {
1958
+ key = scopeKeyOf(event, contentFingerprintOf(event));
1959
+ ttl = MESSAGE_TTL_MS;
1960
+ } else if (event.kind === "notice") {
1961
+ key = noticeKeyOf(event);
1962
+ ttl = EVENT_TTL_MS;
1963
+ } else if (event.kind === "request") {
1964
+ key = requestKeyOf(event);
1965
+ ttl = EVENT_TTL_MS;
1966
+ } else return false;
1967
+ const now = Date.now();
1968
+ const existing = this.#entries.get(key);
1969
+ if (existing?.expiresAt != null && existing.expiresAt >= now) {
1970
+ this.#dropped++;
1971
+ return true;
1972
+ }
1973
+ this.#entries.set(key, { expiresAt: now + ttl });
1974
+ this.#inserts++;
1975
+ if (this.#inserts >= PRUNE_INTERVAL || this.#entries.size > MAX_SIZE) {
1976
+ this.#inserts = 0;
1977
+ this.#prune(now);
1978
+ }
1979
+ return false;
1980
+ }
1981
+ #prune(now) {
1982
+ for (const [key, entry] of this.#entries) if (entry.expiresAt < now) this.#entries.delete(key);
1983
+ }
1984
+ clear() {
1985
+ this.#entries.clear();
1986
+ }
1987
+ };
1988
+
1894
1989
  //#endregion
1895
1990
  //#region src/runtime/mioku-context.ts
1896
1991
  const isObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2015,11 +2110,15 @@ var MiokuContext = class {
2015
2110
  * `onebotv11:message`;返回取消监听函数。
2016
2111
  */
2017
2112
  handle(route, handler) {
2018
- const routes = Array.isArray(route) ? route : [route];
2113
+ const inputRoutes = Array.isArray(route) ? route : [route];
2114
+ const bypassDedup = inputRoutes.some((item) => item.startsWith("!"));
2115
+ const routes = inputRoutes.map((item) => item.startsWith("!") ? item.slice(1) : item);
2019
2116
  const source = `plugin:${this.#options.pluginName}`;
2020
2117
  const handledEvents = new WeakSet();
2118
+ const dedup = bypassDedup || this.#options.dedup === false ? null : new CrossAdapterEventDeduplicator();
2021
2119
  const wrappedHandler = async (event) => {
2022
2120
  if (handledEvents.has(event)) return;
2121
+ if (dedup?.isDuplicate(event)) return;
2023
2122
  handledEvents.add(event);
2024
2123
  await handler(event);
2025
2124
  };
@@ -2505,9 +2604,8 @@ const summarize = (entry) => {
2505
2604
  if (online === total) return `🟢 ${localNum(total)} 实例`;
2506
2605
  return `${online === 0 ? "🔴" : "🟡"} ${localNum(online)}/${localNum(total)} 实例在线`;
2507
2606
  };
2508
- /** `名称@包版本[/实现库 v版本]`,名称走人类可读映射 */
2509
2607
  const formatAdapterName = (entry) => {
2510
- const base = `${displayAdapterId(entry.name)}${entry.version ? `@${entry.version}` : ""}`;
2608
+ const base = `${entry.name}${entry.version ? `@${entry.version}` : ""}`;
2511
2609
  if (!entry.impl) return base;
2512
2610
  const impl = `${entry.impl.name}${entry.impl.version ? ` v${entry.impl.version}` : ""}`;
2513
2611
  return `${base}/${impl}`;
@@ -2698,7 +2796,7 @@ function matchMessageCommands(plugins, text$1) {
2698
2796
  //#endregion
2699
2797
  //#region src/builtin/core/commands/like.ts
2700
2798
  function registerLikeCommand(ctx, getConfig) {
2701
- return ctx.handle("message", async (event) => {
2799
+ return ctx.handle("!message", async (event) => {
2702
2800
  const cfg = getConfig();
2703
2801
  const text$1 = ctx.text(event)?.trim();
2704
2802
  if (!text$1 || event?.user_id === event?.self_id) return;
@@ -4725,96 +4823,6 @@ const loadLocalPlugin = async (jiti$1, name, resolvedPath) => {
4725
4823
  return await loadFromEntry(jiti$1, entry, name);
4726
4824
  };
4727
4825
 
4728
- //#endregion
4729
- //#region src/runtime/cross-adapter-dedup.ts
4730
- const BUCKET_SLACK = 1;
4731
- const PRUNE_INTERVAL = 128;
4732
- const TTL_MS = 15e3;
4733
- const MAX_SIZE = 4096;
4734
- const MAX_SEGMENTS = 16;
4735
- const MAX_TEXT = 256;
4736
- /**
4737
- * 把消息段序列化为跨适配器稳定的内容指纹:
4738
- * 文本/@/表情取内容,媒体段只取类型(url/文件名跨协议不稳定)
4739
- */
4740
- const contentFingerprintOf = (event) => {
4741
- const parts = [];
4742
- for (const seg of event.message) {
4743
- if (parts.length >= MAX_SEGMENTS) break;
4744
- if (seg.type === "reply") continue;
4745
- const data = seg.data ?? {};
4746
- if (seg.type === "text") {
4747
- const text$1 = typeof data.text === "string" ? data.text : "";
4748
- parts.push(`t:${text$1.slice(0, MAX_TEXT)}`);
4749
- } else if (seg.type === "at") {
4750
- const target = data.qq ?? data.target;
4751
- parts.push(`a:${target == null ? "" : String(target)}`);
4752
- } else if (seg.type === "face") parts.push(`f:${data.id == null ? "" : String(data.id)}`);
4753
- else parts.push(seg.type);
4754
- }
4755
- return parts.join("|");
4756
- };
4757
- const scopeKeyOf = (event, content) => [
4758
- event.self_id ?? "",
4759
- event.message_type ?? "",
4760
- event.group_id ?? event.user_id ?? "",
4761
- event.user_id ?? "",
4762
- content
4763
- ].join("|");
4764
- /**
4765
- * 跨适配器消息去重(L2)
4766
- */
4767
- var CrossAdapterMessageDeduplicator = class {
4768
- #entries = new Map();
4769
- #inserts = 0;
4770
- #dropped = 0;
4771
- /** 本次运行以来被丢弃的跨适配器重复消息数 */
4772
- get dropped() {
4773
- return this.#dropped;
4774
- }
4775
- isDuplicate(event) {
4776
- if (event.kind !== "message") return false;
4777
- const messageEvent = event;
4778
- const adapter = messageEvent.identity?.adapter ?? "";
4779
- const now = Date.now();
4780
- const bucket = Math.floor((messageEvent.time ?? now) / 1e3);
4781
- const key = scopeKeyOf(messageEvent, contentFingerprintOf(messageEvent));
4782
- const existing = this.#entries.get(key);
4783
- if (existing) {
4784
- if (Math.abs(existing.bucket - bucket) <= BUCKET_SLACK) {
4785
- if (existing.adapter !== adapter) {
4786
- this.#dropped++;
4787
- return true;
4788
- }
4789
- existing.bucket = bucket;
4790
- existing.at = now;
4791
- return false;
4792
- }
4793
- existing.bucket = bucket;
4794
- existing.at = now;
4795
- existing.adapter = adapter;
4796
- return false;
4797
- }
4798
- this.#entries.set(key, {
4799
- adapter,
4800
- bucket,
4801
- at: now
4802
- });
4803
- this.#inserts++;
4804
- if (this.#inserts >= PRUNE_INTERVAL || this.#entries.size > MAX_SIZE) {
4805
- this.#inserts = 0;
4806
- this.#prune(now);
4807
- }
4808
- return false;
4809
- }
4810
- #prune(now) {
4811
- for (const [key, entry] of this.#entries) if (now - entry.at > TTL_MS) this.#entries.delete(key);
4812
- }
4813
- clear() {
4814
- this.#entries.clear();
4815
- }
4816
- };
4817
-
4818
4826
  //#endregion
4819
4827
  //#region src/runtime/runtime.ts
4820
4828
  const BUILTIN_PLUGINS$1 = [...BUILTIN_PLUGINS];
@@ -4835,7 +4843,7 @@ var MiokuRuntime = class {
4835
4843
  #enabledPlugins = new Map();
4836
4844
  #driverFactory;
4837
4845
  #builtinPlugins;
4838
- #crossDedup;
4846
+ #dedupEnabled;
4839
4847
  #started = false;
4840
4848
  #stopped = false;
4841
4849
  constructor(options) {
@@ -4843,6 +4851,7 @@ var MiokuRuntime = class {
4843
4851
  this.#logger = options.logger;
4844
4852
  this.#driverFactory = options.driverFactory ?? (() => createDefaultDriver());
4845
4853
  this.#builtinPlugins = options.builtinPlugins ?? BUILTIN_PLUGINS$1;
4854
+ this.#dedupEnabled = options.dedup?.crossAdapter !== false;
4846
4855
  this.#driver = this.#driverFactory();
4847
4856
  this.#bus = new EventBus();
4848
4857
  this.#bus.setLogger((level, message, detail) => {
@@ -4852,7 +4861,6 @@ var MiokuRuntime = class {
4852
4861
  });
4853
4862
  this.#bots = new BotRegistry();
4854
4863
  this.#capabilities = new CapabilityRegistry();
4855
- this.#crossDedup = options.dedup?.crossAdapter === false ? null : new CrossAdapterMessageDeduplicator();
4856
4864
  }
4857
4865
  get cwd() {
4858
4866
  return this.#cwd;
@@ -4973,8 +4981,7 @@ var MiokuRuntime = class {
4973
4981
  driver: this.#driver,
4974
4982
  capabilities: this.#capabilities,
4975
4983
  logger: this.#logger.child({ adapter: state.definition.name }),
4976
- emit: (event) => this.#emitLifecycle(event),
4977
- crossDedup: this.#crossDedup
4984
+ emit: (event) => this.#emitLifecycle(event)
4978
4985
  });
4979
4986
  }
4980
4987
  async #loadPlugin(plugin, type) {
@@ -4988,6 +4995,7 @@ var MiokuRuntime = class {
4988
4995
  config: botConfig,
4989
4996
  logger: this.#logger.child({ plugin: plugin.name }),
4990
4997
  priority: plugin.priority ?? 100,
4998
+ dedup: this.#dedupEnabled,
4991
4999
  getAdapter: (name) => this.getAdapter(name),
4992
5000
  listAdapters: () => this.adapters,
4993
5001
  onUpdateConfig: async (updater) => {
@@ -5479,6 +5487,7 @@ exports.addService = addService;
5479
5487
  exports.applyLogLevel = applyLogLevel;
5480
5488
  exports.asMessage = asMessage;
5481
5489
  exports.atOf = atOf;
5490
+ exports.avatarGet = avatarGet;
5482
5491
  exports.avatarSet = avatarSet;
5483
5492
  exports.base64Decode = base64Decode;
5484
5493
  exports.base64Encode = base64Encode;