()
| 92 | } |
| 93 | |
| 94 | function discoverBundledPluginManifests(): BundledPluginManifest[] { |
| 95 | if (_bundledPluginManifestCache) return _bundledPluginManifestCache; |
| 96 | |
| 97 | const manifests = new Map<string, BundledPluginManifest>(); |
| 98 | |
| 99 | for (const extensionsDir of getOpenClawExtensionsRoots()) { |
| 100 | try { |
| 101 | if (!existsSync(extensionsDir)) { |
| 102 | continue; |
| 103 | } |
| 104 | |
| 105 | for (const entry of readdirSync(extensionsDir, { withFileTypes: true })) { |
| 106 | if (!entry.isDirectory()) continue; |
| 107 | |
| 108 | const manifestPath = join(extensionsDir, entry.name, 'openclaw.plugin.json'); |
| 109 | if (!existsSync(manifestPath)) continue; |
| 110 | |
| 111 | try { |
| 112 | const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8')) as { |
| 113 | id?: unknown; |
| 114 | enabledByDefault?: unknown; |
| 115 | providers?: unknown; |
| 116 | legacyPluginIds?: unknown; |
| 117 | }; |
| 118 | if (typeof parsed.id !== 'string' || !parsed.id.trim()) { |
| 119 | continue; |
| 120 | } |
| 121 | |
| 122 | const existing = manifests.get(parsed.id) ?? { |
| 123 | id: parsed.id, |
| 124 | enabledByDefault: false, |
| 125 | providers: [], |
| 126 | legacyPluginIds: [], |
| 127 | }; |
| 128 | |
| 129 | const providers = Array.isArray(parsed.providers) |
| 130 | ? parsed.providers.filter((provider): provider is string => typeof provider === 'string' && provider.trim().length > 0) |
| 131 | : []; |
| 132 | const legacyPluginIds = Array.isArray(parsed.legacyPluginIds) |
| 133 | ? parsed.legacyPluginIds.filter((pluginId): pluginId is string => typeof pluginId === 'string' && pluginId.trim().length > 0) |
| 134 | : []; |
| 135 | |
| 136 | existing.enabledByDefault = existing.enabledByDefault || parsed.enabledByDefault === true; |
| 137 | existing.providers = Array.from(new Set([...existing.providers, ...providers])); |
| 138 | existing.legacyPluginIds = Array.from(new Set([...existing.legacyPluginIds, ...legacyPluginIds])); |
| 139 | |
| 140 | manifests.set(parsed.id, existing); |
| 141 | } catch { |
| 142 | // Malformed manifest — skip silently |
| 143 | } |
| 144 | } |
| 145 | } catch { |
| 146 | // Extension directory not found or unreadable — ignore |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | _bundledPluginManifestCache = Array.from(manifests.values()); |
| 151 | return _bundledPluginManifestCache; |
no test coverage detected