(options = {})
| 274 | } |
| 275 | |
| 276 | function createPluginMarketplace(options = {}) { |
| 277 | const projectRoot = path.resolve(options.projectRoot || process.cwd()); |
| 278 | const pluginName = options.pluginName || 'citadel'; |
| 279 | const marketplacePath = path.join(projectRoot, '.agents', 'plugins', 'marketplace.json'); |
| 280 | const pluginPath = options.pluginPath || './.agents'; |
| 281 | const displayName = options.displayName || 'Citadel Harness'; |
| 282 | const pluginRoot = path.join(projectRoot, pluginPath.replace(/^\.\//, '')); |
| 283 | const pluginManifestPath = path.join(pluginRoot, '.codex-plugin', 'plugin.json'); |
| 284 | let pluginManifest = {}; |
| 285 | try { pluginManifest = JSON.parse(fs.readFileSync(pluginManifestPath, 'utf8')); } catch { /* check below reports absence */ } |
| 286 | const pluginRecord = { |
| 287 | name: pluginName, |
| 288 | source: { |
| 289 | source: 'local', |
| 290 | path: pluginPath, |
| 291 | }, |
| 292 | policy: { |
| 293 | installation: 'AVAILABLE', |
| 294 | authentication: 'ON_INSTALL', |
| 295 | }, |
| 296 | category: 'Developer Tools', |
| 297 | interface: { |
| 298 | displayName, |
| 299 | }, |
| 300 | }; |
| 301 | for (const field of ['version', 'description', 'repository']) { |
| 302 | if (typeof pluginManifest[field] === 'string' && pluginManifest[field]) pluginRecord[field] = pluginManifest[field]; |
| 303 | } |
| 304 | if (fs.existsSync(path.join(projectRoot, 'citadel-metadata.json'))) { |
| 305 | pluginRecord.metadata = '../../citadel-metadata.json'; |
| 306 | } |
| 307 | const marketplace = { |
| 308 | name: options.marketplaceName || 'citadel-local', |
| 309 | interface: { |
| 310 | displayName: options.marketplaceDisplayName || 'Citadel Local Plugins', |
| 311 | }, |
| 312 | plugins: [pluginRecord], |
| 313 | }; |
| 314 | |
| 315 | const checks = [ |
| 316 | { |
| 317 | id: 'plugin-manifest', |
| 318 | pass: fs.existsSync(pluginManifestPath), |
| 319 | detail: pluginManifestPath, |
| 320 | }, |
| 321 | { |
| 322 | id: 'source-path-relative', |
| 323 | pass: pluginPath.startsWith('./') && !pluginPath.includes('..'), |
| 324 | detail: pluginPath, |
| 325 | }, |
| 326 | ]; |
| 327 | |
| 328 | const result = { |
| 329 | projectRoot, |
| 330 | marketplacePath, |
| 331 | marketplace, |
| 332 | checks, |
| 333 | pass: checks.every((check) => check.pass), |
no test coverage detected