diff --git a/TODO.md b/TODO.md index c00219d..57c2626 100644 --- a/TODO.md +++ b/TODO.md @@ -124,6 +124,7 @@ This file tracks larger Lumi work that cannot safely be completed in one pass. K - Review localization/translation keys if present so simplified wording remains consistent across languages. ## Done +- 2026-06-17: Fixed update metadata for renamed Economy plugins so legacy installed rows are folded into canonical `economy-*` plugin update rows instead of appearing as separate installs; bumped core to v0.1.8. - 2026-06-17: Renamed all remaining Economy internals from the old misspelled IDs/paths/tables to `economy-*`, added startup migration for legacy plugin rows, settings, command usage IDs, tables, uploads, asset paths, old URLs, and bumped core/plugin patch versions. - 2026-06-17: Fixed user-facing Economy spelling, restored `/stats/{username}` Compare toggling, linked Top commands run leaderboard entries to `/commands`, and bumped core/plugin patch versions. - 2026-06-17: Fixed custom command Edit buttons and `/commands` Copy Link / expand buttons with delegated handlers, clipboard fallback, and v0.1.5 patch bump. diff --git a/package-lock.json b/package-lock.json index 0a7936a..2f67b90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lumi-bot", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lumi-bot", - "version": "0.1.7", + "version": "0.1.8", "dependencies": { "adm-zip": "^0.5.12", "better-sqlite3": "^11.5.0", diff --git a/package.json b/package.json index c82a39f..af38cd5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lumi-bot", - "version": "0.1.7", + "version": "0.1.8", "private": true, "type": "commonjs", "scripts": { diff --git a/src/services/plugins.js b/src/services/plugins.js index 70bcd44..9f38a2d 100644 --- a/src/services/plugins.js +++ b/src/services/plugins.js @@ -16,6 +16,20 @@ const economyCommandAliases = [ { from: `${legacyEconomyStem}-games:coinflip`, to: "economy-games:coinflip" }, { from: `${legacyEconomyStem}-games:mystery`, to: "economy-games:mystery" } ]; +const pluginCanonicalAliases = Object.fromEntries( + Object.entries(economyPluginAliases).flatMap(([canonicalId, legacyIds]) => + legacyIds.map((legacyId) => [legacyId, canonicalId]) + ) +); + +function canonicalPluginId(id) { + const raw = String(id || "").trim(); + return pluginCanonicalAliases[raw] || raw; +} + +function pluginLegacyIds(id) { + return economyPluginAliases[canonicalPluginId(id)] || []; +} function readJson(filePath) { const raw = fs.readFileSync(filePath, "utf8"); @@ -27,7 +41,7 @@ function scanPluginDirectories() { return []; } const entries = fs.readdirSync(pluginsDir, { withFileTypes: true }); - const plugins = []; + const plugins = new Map(); for (const entry of entries) { if (!entry.isDirectory()) { continue; @@ -41,20 +55,33 @@ function scanPluginDirectories() { } try { const manifest = readJson(manifestPath); - plugins.push({ - id: manifest.id, + const id = canonicalPluginId(manifest.id); + const plugin = { + id, + manifestId: manifest.id, name: manifest.name || manifest.id, version: manifest.version || "0.0.0", description: manifest.description || "", main: manifest.main || "index.js", dir: path.join(pluginsDir, entry.name), - legacyIds: economyPluginAliases[manifest.id] || [] - }); + legacyIds: pluginLegacyIds(manifest.id) + }; + const existing = plugins.get(id); + if (!existing || isCanonicalPluginDirectory(plugin, entry.name, existing)) { + plugins.set(id, plugin); + } } catch { continue; } } - return plugins; + return Array.from(plugins.values()); +} + +function isCanonicalPluginDirectory(plugin, directoryName, existing) { + if (directoryName === plugin.id || plugin.manifestId === plugin.id) { + return true; + } + return existing.manifestId !== existing.id && existing.dir !== path.join(pluginsDir, existing.id); } function syncPluginRegistry() { @@ -322,5 +349,7 @@ module.exports = { stopPlugins, installFromGit, updatePluginFromGit, - createLocalPlugin + createLocalPlugin, + canonicalPluginId, + pluginLegacyIds }; diff --git a/src/services/update-index.js b/src/services/update-index.js index af33115..952feee 100644 --- a/src/services/update-index.js +++ b/src/services/update-index.js @@ -1,7 +1,7 @@ const fs = require("fs"); const path = require("path"); const { getSetting } = require("./settings"); -const { scanPluginDirectories, getPlugins } = require("./plugins"); +const { scanPluginDirectories, getPlugins, canonicalPluginId } = require("./plugins"); const { parseSemver, compareSemver, @@ -112,19 +112,22 @@ function snapshotAvailability(kind, id = null) { function localPluginCandidates(registry) { const candidates = new Map(); for (const plugin of scanPluginDirectories()) { - candidates.set(plugin.id, { ...plugin, installed: true }); + const id = canonicalPluginId(plugin.id); + candidates.set(id, { ...plugin, id, installed: true }); } for (const plugin of registry.values()) { - if (isLumiAiToolId(plugin.id)) { + const id = canonicalPluginId(plugin.id); + if (isLumiAiToolId(id)) { continue; } - if (candidates.has(plugin.id)) { + if (candidates.has(id)) { continue; } const pluginPath = plugin.path || ""; const installed = Boolean(pluginPath && fs.existsSync(path.join(pluginPath, "plugin.json"))); - candidates.set(plugin.id, { - id: plugin.id, + candidates.set(id, { + id, + registry_id: plugin.id, name: plugin.name || plugin.id, version: plugin.version || "0.0.0", description: "", @@ -136,6 +139,18 @@ function localPluginCandidates(registry) { return candidates; } +function pluginRegistry() { + const registry = new Map(); + for (const plugin of getPlugins()) { + const id = canonicalPluginId(plugin.id); + const existing = registry.get(id); + if (!existing || plugin.id === id) { + registry.set(id, { ...plugin, id, registry_id: plugin.id }); + } + } + return registry; +} + function localToolCandidates() { const tools = new Map(); const pluginsPath = path.join(repoRoot, "plugins"); @@ -299,7 +314,7 @@ function getUpdateStatus(options = {}) { sourceBranch, channel: requestedSource === "experimental" ? "experimental" : "stable" }); - const registry = new Map(getPlugins().map((plugin) => [plugin.id, plugin])); + const registry = pluginRegistry(); const remoteDirs = new Set(reader.listPluginDirs()); const remotePluginDirs = new Set(); const remoteTools = new Map(); @@ -320,11 +335,12 @@ function getUpdateStatus(options = {}) { continue; } remotePluginDirs.add(pluginId); - if (candidates.has(pluginId)) { + const canonicalRemoteId = canonicalPluginId(pluginId); + if (candidates.has(canonicalRemoteId)) { continue; } - candidates.set(pluginId, { - id: pluginId, + candidates.set(canonicalRemoteId, { + id: canonicalRemoteId, name: manifest.name || pluginId, version: "0.0.0", description: manifest.description || "",