@devilsdev/rag-pipeline-utils 2.4.5 → 2.5.0

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.
Files changed (225) hide show
  1. package/.ragrc.schema.json +83 -192
  2. package/CHANGELOG.md +345 -0
  3. package/README.md +418 -117
  4. package/SECURITY.md +5 -5
  5. package/dist/experimental.cjs +2 -0
  6. package/dist/experimental.d.ts +1 -0
  7. package/dist/experimental.mjs +5 -0
  8. package/dist/index.cjs +4992 -1242
  9. package/dist/index.d.ts +80 -2
  10. package/dist/index.mjs +1 -1
  11. package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
  12. package/dist/types/agentic/iterative-retriever.d.ts +85 -0
  13. package/dist/types/agentic/query-planner.d.ts +145 -0
  14. package/dist/types/agentic/self-critique.d.ts +45 -0
  15. package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
  16. package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
  17. package/dist/types/ai/multimodal-processing.d.ts +32 -0
  18. package/dist/types/ai/multimodal.d.ts +39 -0
  19. package/dist/types/ai/retrieval-engine.d.ts +37 -0
  20. package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
  21. package/dist/types/chunking/chunking-engine.d.ts +70 -0
  22. package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
  23. package/dist/types/chunking/strategies/recursive.d.ts +20 -0
  24. package/dist/types/chunking/strategies/semantic.d.ts +19 -0
  25. package/dist/types/chunking/strategies/sentence.d.ts +13 -0
  26. package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
  27. package/dist/types/citation/citation-tracker.d.ts +64 -0
  28. package/dist/types/citation/hallucination-detector.d.ts +40 -0
  29. package/dist/types/citation/source-mapper.d.ts +37 -0
  30. package/dist/types/connectors/anthropic-connector.d.ts +92 -0
  31. package/dist/types/connectors/base-connector.d.ts +87 -0
  32. package/dist/types/connectors/cohere-connector.d.ts +89 -0
  33. package/dist/types/connectors/connector-utils.d.ts +110 -0
  34. package/dist/types/connectors/local-embedder.d.ts +86 -0
  35. package/dist/types/connectors/memory-retriever.d.ts +91 -0
  36. package/dist/types/connectors/ollama-connector.d.ts +110 -0
  37. package/dist/types/connectors/openai-connector.d.ts +114 -0
  38. package/dist/types/connectors/rag-prompt.d.ts +40 -0
  39. package/dist/types/cost/cost-calculator.d.ts +152 -0
  40. package/dist/types/cost/cost-tracker.d.ts +126 -0
  41. package/dist/types/cost/token-budget.d.ts +110 -0
  42. package/dist/types/dag/core/error-context.d.ts +239 -0
  43. package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
  44. package/dist/types/dag/core/topology-validation.d.ts +226 -0
  45. package/dist/types/dag/dag-engine.d.ts +156 -0
  46. package/dist/types/dag/dag-node.d.ts +37 -0
  47. package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
  48. package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
  49. package/dist/types/debug/execution-tracer.d.ts +57 -0
  50. package/dist/types/debug/trace-inspector.d.ts +31 -0
  51. package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
  52. package/dist/types/enterprise/data-governance.d.ts +203 -0
  53. package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
  54. package/dist/types/enterprise/sso-integration.d.ts +420 -0
  55. package/dist/types/evaluate/context-metrics.d.ts +55 -0
  56. package/dist/types/evaluate/faithfulness.d.ts +41 -0
  57. package/dist/types/evaluate/groundedness.d.ts +60 -0
  58. package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
  59. package/dist/types/evaluate/relevance.d.ts +21 -0
  60. package/dist/types/evaluate/scoring.d.ts +123 -0
  61. package/dist/types/experimental.d.ts +15 -0
  62. package/dist/types/graph/entity-extractor.d.ts +84 -0
  63. package/dist/types/graph/graph-index.d.ts +49 -0
  64. package/dist/types/graph/graph-retriever.d.ts +81 -0
  65. package/dist/types/graph/knowledge-graph.d.ts +129 -0
  66. package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
  67. package/dist/types/guardrails/injection-detector.d.ts +47 -0
  68. package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
  69. package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
  70. package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
  71. package/dist/types/mcp/mcp-server.d.ts +85 -0
  72. package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
  73. package/dist/types/reranker/cascade-reranker.d.ts +40 -0
  74. package/dist/types/reranker/embedding-reranker.d.ts +77 -0
  75. package/dist/types/reranker/scoring-reranker.d.ts +41 -0
  76. package/dist/types/retrieval/bm25-search.d.ts +43 -0
  77. package/dist/types/retrieval/file-vector-store.d.ts +87 -0
  78. package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
  79. package/dist/types/retrieval/rank-fusion.d.ts +10 -0
  80. package/dist/types/security/jwt-config.d.ts +80 -0
  81. package/dist/types/security/jwt-validator.d.ts +195 -0
  82. package/dist/types/security/plugin-sandbox.d.ts +158 -0
  83. package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
  84. package/dist/types/security/validator.d.ts +5 -0
  85. package/dist/types/streaming/embedding-stream.d.ts +65 -0
  86. package/dist/types/streaming/sse-adapter.d.ts +59 -0
  87. package/dist/types/streaming/stream-router.d.ts +88 -0
  88. package/dist/types/streaming/streaming-embedder.d.ts +99 -0
  89. package/dist/types/streaming/websocket-adapter.d.ts +71 -0
  90. package/dist/types/templates/scaffold.d.ts +55 -0
  91. package/dist/types/templates/template-registry.d.ts +63 -0
  92. package/dist/types/utils/embed-query.d.ts +20 -0
  93. package/dist/types/utils/logger.d.ts +50 -0
  94. package/dist/types/utils/rate-limiter.d.ts +66 -0
  95. package/dist/types/utils/retry.d.ts +27 -0
  96. package/dist/types/utils/run-abort.d.ts +34 -0
  97. package/dist/types/utils/secure-logging.d.ts +51 -0
  98. package/dist/types/utils/structured-logger.d.ts +126 -0
  99. package/package.json +53 -29
  100. package/src/agentic/agentic-pipeline.js +75 -7
  101. package/src/agentic/iterative-retriever.js +47 -10
  102. package/src/agentic/query-planner.js +329 -78
  103. package/src/citation/citation-tracker.js +6 -4
  104. package/src/citation/hallucination-detector.js +2 -2
  105. package/src/citation/source-mapper.js +15 -28
  106. package/src/cli/commands/ai.js +101 -0
  107. package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
  108. package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
  109. package/src/cli/commands/plugin-hub/local-commands.js +143 -0
  110. package/src/cli/commands/plugin-hub.js +96 -36
  111. package/src/cli/diagnostics/config-checker.js +76 -63
  112. package/src/cli/diagnostics/environment-checker.js +62 -31
  113. package/src/cli/diagnostics/plugin-checker.js +36 -89
  114. package/src/cli/doctor-command.js +39 -5
  115. package/src/cli/enhanced-cli-commands.js +72 -217
  116. package/src/cli/handlers/ai-handler.js +364 -0
  117. package/src/cli/handlers/diagnostics-handler.js +78 -31
  118. package/src/cli/handlers/ingest-handler.js +28 -22
  119. package/src/cli/handlers/init-handler.js +148 -25
  120. package/src/cli/handlers/query-handler.js +41 -26
  121. package/src/cli/handlers/run-handler.js +16 -167
  122. package/src/cli/handlers/serve-handler.js +60 -0
  123. package/src/cli/services/bench-stats.js +54 -0
  124. package/src/cli/services/benchmark.js +161 -0
  125. package/src/cli/services/feedback.js +155 -0
  126. package/src/cli/services/rag-service.js +322 -0
  127. package/src/cli/shared/cli-utils.js +24 -7
  128. package/src/cli/shared/config-context.js +21 -0
  129. package/src/cli/wizard/config-builder.js +81 -0
  130. package/src/cli/wizard/init-wizard.js +617 -0
  131. package/src/config/component-factory.js +125 -0
  132. package/src/config/load-config.js +29 -10
  133. package/src/config/load-plugin-config.js +46 -2
  134. package/src/connectors/anthropic-connector.js +93 -55
  135. package/src/connectors/base-connector.js +74 -19
  136. package/src/connectors/cohere-connector.js +88 -32
  137. package/src/connectors/connector-utils.js +302 -0
  138. package/src/connectors/local-embedder.js +43 -5
  139. package/src/connectors/memory-retriever.js +108 -15
  140. package/src/connectors/ollama-connector.js +83 -49
  141. package/src/connectors/openai-connector.js +108 -44
  142. package/src/connectors/rag-prompt.js +88 -0
  143. package/src/core/create-pipeline.js +209 -60
  144. package/src/core/plugin-registry.js +87 -11
  145. package/src/cost/cost-calculator.js +205 -32
  146. package/src/dev/dev-server.js +166 -11
  147. package/src/dev/hot-reload.js +40 -1
  148. package/src/ecosystem/plugin-checks.js +181 -0
  149. package/src/ecosystem/plugin-hub.js +50 -125
  150. package/src/ecosystem/plugin-installer.js +298 -0
  151. package/src/ecosystem/plugin-probe.js +127 -0
  152. package/src/ecosystem/plugin-scanner.js +185 -0
  153. package/src/ecosystem/tar-extract.js +114 -0
  154. package/src/enterprise/audit-logging.js +158 -29
  155. package/src/enterprise/sso-integration.js +715 -228
  156. package/src/evaluate/context-metrics.js +28 -16
  157. package/src/evaluate/evaluator.js +3 -3
  158. package/src/evaluate/faithfulness.js +18 -14
  159. package/src/evaluate/groundedness.js +43 -12
  160. package/src/evaluate/pipeline-evaluator.js +35 -8
  161. package/src/evaluate/scoring.js +251 -3
  162. package/src/experimental.js +71 -0
  163. package/src/guardrails/guardrails-pipeline.js +69 -7
  164. package/src/guardrails/injection-detector.js +283 -0
  165. package/src/guardrails/post-generation-guard.js +19 -0
  166. package/src/guardrails/pre-retrieval-guard.js +36 -36
  167. package/src/guardrails/retrieval-guard.js +58 -8
  168. package/src/index.js +36 -1
  169. package/src/loader/document-reader.js +91 -0
  170. package/src/reranker/cascade-reranker.js +3 -0
  171. package/src/reranker/embedding-reranker.js +136 -21
  172. package/src/reranker/llm-reranker.js +79 -12
  173. package/src/reranker/scoring-reranker.js +3 -0
  174. package/src/retrieval/bm25-search.js +12 -6
  175. package/src/retrieval/file-vector-store.js +212 -0
  176. package/src/retrieval/hybrid-retriever.js +22 -19
  177. package/src/security/jwt-config.js +13 -2
  178. package/src/security/jwt-validator.js +134 -30
  179. package/src/security/plugin-sandbox.js +18 -1
  180. package/src/security/plugin-signature-verifier.js +245 -70
  181. package/src/utils/deprecate.js +69 -0
  182. package/src/utils/embed-query.js +33 -0
  183. package/src/utils/input-sanitizer.js +19 -4
  184. package/src/utils/logger.js +34 -4
  185. package/src/utils/run-abort.js +102 -0
  186. package/src/ai/federated-learning.js +0 -211
  187. package/src/ai/federation.js +0 -71
  188. package/src/ai/index.js +0 -22
  189. package/src/ai/model-trainer.js +0 -217
  190. package/src/ai/model-training.js +0 -202
  191. package/src/ai/multimodal/multi-modal-processor.js +0 -345
  192. package/src/ai/orchestrator.js +0 -65
  193. package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
  194. package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
  195. package/src/cli/commands/ai-ml/federated-commands.js +0 -207
  196. package/src/cli/commands/ai-ml/index.js +0 -17
  197. package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
  198. package/src/cli/commands/ai-ml/shared.js +0 -51
  199. package/src/cli/commands/ai-ml/training-commands.js +0 -228
  200. package/src/cli/commands/ai-ml.js +0 -386
  201. package/src/cli/commands/docs.js +0 -228
  202. package/src/cli/commands/dx.js +0 -538
  203. package/src/cli/commands/wizard.js +0 -627
  204. package/src/cli/input-sanitization-middleware.js +0 -356
  205. package/src/cli/interactive-wizard.js +0 -373
  206. package/src/cli/templates/development.template.json +0 -64
  207. package/src/cli/templates/minimal.template.json +0 -33
  208. package/src/cli/templates/production.template.json +0 -104
  209. package/src/cli/templates/testing.template.json +0 -45
  210. package/src/cli/wizard/configuration.js +0 -148
  211. package/src/cli/wizard/observability.js +0 -79
  212. package/src/cli/wizard/performance.js +0 -76
  213. package/src/cli/wizard/plugin-selection.js +0 -297
  214. package/src/cli/wizard/preview-save.js +0 -172
  215. package/src/cli/wizard/project-setup.js +0 -66
  216. package/src/core/pipeline-factory.js +0 -59
  217. package/src/dag/enhanced-dag-engine.js +0 -894
  218. package/src/dx/index.js +0 -40
  219. package/src/dx/integration-templates.js +0 -662
  220. package/src/dx/performance-profiler.js +0 -968
  221. package/src/dx/realtime-debugger.js +0 -755
  222. package/src/dx/visual-pipeline-builder.js +0 -578
  223. package/src/ingest.js +0 -59
  224. package/src/pipeline/pipeline.js +0 -18
  225. package/src/query.js +0 -117
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * `rag-pipeline ai ...`: training, benchmarking, relevance feedback and file
5
+ * inspection built on the real pipeline components (.ragrc.json + index).
6
+ */
7
+
8
+ const { Command, Option } = require("commander");
9
+ const { parsePositiveInteger } = require("../shared/cli-utils.js");
10
+ const {
11
+ handleTrainEmbedding,
12
+ handleBenchmark,
13
+ handleFeedback,
14
+ handleSearch,
15
+ handleMultimodalProcess,
16
+ } = require("../handlers/ai-handler.js");
17
+
18
+ /**
19
+ * @param {() => object} getGlobalOptions - Returns program.opts()
20
+ * @returns {Command}
21
+ */
22
+ function createAiCommand(getGlobalOptions) {
23
+ const ai = new Command("ai").description(
24
+ "Train the local embedder, benchmark queries, relevance feedback, file inspection",
25
+ );
26
+
27
+ const train = ai
28
+ .command("train")
29
+ .description("Fit corpus-trained components");
30
+ train
31
+ .command("embedding")
32
+ .description(
33
+ "Refit the local TF-IDF embedder (on the index, or on [path]) and re-embed the index",
34
+ )
35
+ .argument(
36
+ "[path]",
37
+ "Train on documents from this file or directory instead",
38
+ )
39
+ .action(async (inputPath) => {
40
+ await handleTrainEmbedding(getGlobalOptions(), inputPath);
41
+ });
42
+
43
+ ai.command("benchmark")
44
+ .description(
45
+ "Measure query latency against the configured pipeline and index",
46
+ )
47
+ .option(
48
+ "--queries <file>",
49
+ "Queries file: JSON array of strings or one per line (default: sampled from the index)",
50
+ )
51
+ .option("--runs <n>", "Passes over the query set", parsePositiveInteger, 3)
52
+ .option("--top-k <n>", "Passages to retrieve", parsePositiveInteger, 3)
53
+ .option("--json", "Print the measurements as JSON")
54
+ .action(async (options) => {
55
+ await handleBenchmark(getGlobalOptions(), options);
56
+ });
57
+
58
+ ai.command("feedback")
59
+ .description(
60
+ "Record whether an indexed chunk was a good result for a question",
61
+ )
62
+ .argument("<question>", "Question the chunk was returned for")
63
+ .argument("<docId>", "Chunk id as shown by `ai search` (e.g. docs/a.md#0)")
64
+ .addOption(
65
+ new Option("--rating <rating>", "Relevance vote")
66
+ .choices(["up", "down"])
67
+ .makeOptionMandatory(),
68
+ )
69
+ .action(async (question, docId, options) => {
70
+ await handleFeedback(getGlobalOptions(), question, docId, options);
71
+ });
72
+
73
+ ai.command("search")
74
+ .description(
75
+ "Retrieve passages re-scored with stored feedback (retrieval only)",
76
+ )
77
+ .argument("<question>", "Question text")
78
+ .option("--top-k <n>", "Passages to return", parsePositiveInteger, 3)
79
+ .option("--json", "Print the results as JSON")
80
+ .action(async (question, options) => {
81
+ await handleSearch(getGlobalOptions(), question, options);
82
+ });
83
+
84
+ const multimodal = ai
85
+ .command("multimodal")
86
+ .description("Inspect files before ingesting");
87
+ multimodal
88
+ .command("process")
89
+ .description(
90
+ "Report type, characters and chunks for text files (images need a vision model)",
91
+ )
92
+ .argument("<path>", "File or directory")
93
+ .option("--json", "Print the report as JSON")
94
+ .action(async (inputPath, options) => {
95
+ await handleMultimodalProcess(getGlobalOptions(), inputPath, options);
96
+ });
97
+
98
+ return ai;
99
+ }
100
+
101
+ module.exports = { createAiCommand };
@@ -6,6 +6,12 @@
6
6
  const chalk = require("chalk");
7
7
 
8
8
  function setupEventListeners(hub, certification) {
9
+ // The hub emits 'error' and then throws a descriptive error that each
10
+ // command reports. Without a listener, EventEmitter would throw its own
11
+ // "Unhandled error" first and hide that message.
12
+ hub.on("error", () => {});
13
+ certification.on("error", () => {});
14
+
9
15
  // Hub events
10
16
  hub.on("install_start", (data) => {
11
17
  console.log(
@@ -69,34 +69,6 @@ async function searchPlugins(hub, query, _options) {
69
69
  }
70
70
  }
71
71
 
72
- async function installPlugin(hub, plugin, _options) {
73
- try {
74
- const installOptions = {
75
- securityScan: _options.securityScan,
76
- requireCertified: _options.requireCertified,
77
- sandboxTimeout: _options.sandboxTimeout,
78
- };
79
-
80
- const result = await hub.installPlugin(
81
- plugin,
82
- _options.version,
83
- installOptions,
84
- );
85
-
86
- console.log(chalk.green("\n✅ Installation completed successfully!")); // eslint-disable-line no-console
87
- console.log(chalk.blue(`Plugin: ${result.pluginInfo.name}`)); // eslint-disable-line no-console
88
- console.log(chalk.blue(`Version: ${result.version}`)); // eslint-disable-line no-console
89
- console.log(chalk.blue(`Install Path: ${result.installPath}`)); // eslint-disable-line no-console
90
-
91
- if (result.pluginInfo.certified) {
92
- console.log(chalk.green("🏆 This plugin is certified!")); // eslint-disable-line no-console
93
- }
94
- } catch (error) {
95
- console.error(chalk.red(`Installation failed: ${error.message}`)); // eslint-disable-line no-console
96
- process.exit(1);
97
- }
98
- }
99
-
100
72
  async function getPluginInfo(hub, plugin) {
101
73
  const spinner = ora("Fetching plugin information...").start();
102
74
 
@@ -170,50 +142,6 @@ async function getPluginInfo(hub, plugin) {
170
142
  }
171
143
  }
172
144
 
173
- async function listInstalledPlugins(hub, _options) {
174
- const spinner = ora("Loading installed plugins...").start();
175
-
176
- try {
177
- const installed = await hub.getInstalledPlugins();
178
- spinner.stop();
179
-
180
- if (installed.length === 0) {
181
- console.log(chalk.yellow("No plugins installed.")); // eslint-disable-line no-console
182
- return;
183
- }
184
-
185
- if (_options.format === "json") {
186
- console.log(JSON.stringify(installed, null, 2)); // eslint-disable-line no-console
187
- return;
188
- }
189
-
190
- console.log(chalk.green(`${installed.length} plugins installed:\n`)); // eslint-disable-line no-console
191
-
192
- const table = new Table({
193
- head: ["Name", "Version", "Type", "Last Used", "Status"],
194
- colWidths: [25, 12, 15, 15, 10],
195
- });
196
-
197
- for (const plugin of installed) {
198
- const lastUsed = plugin.lastUsed
199
- ? new Date(plugin.lastUsed).toLocaleDateString()
200
- : chalk.gray("Never");
201
-
202
- const status = plugin.certified
203
- ? chalk.green("Certified")
204
- : chalk.gray("Standard");
205
-
206
- table.push([plugin.name, plugin.version, plugin._type, lastUsed, status]);
207
- }
208
-
209
- console.log(table.toString()); // eslint-disable-line no-console
210
- } catch (error) {
211
- spinner.stop();
212
- console.error(chalk.red(`Failed to list plugins: ${error.message}`)); // eslint-disable-line no-console
213
- process.exit(1);
214
- }
215
- }
216
-
217
145
  async function publishPlugin(hub, pluginPath, _options) {
218
146
  if (_options.dryRun) {
219
147
  console.log(
@@ -282,9 +210,7 @@ async function getTrendingPlugins(hub, _options) {
282
210
 
283
211
  module.exports = {
284
212
  searchPlugins,
285
- installPlugin,
286
213
  getPluginInfo,
287
- listInstalledPlugins,
288
214
  publishPlugin,
289
215
  getTrendingPlugins,
290
216
  };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * `hub install` and `hub certify submit|check`: real plugin installation and
3
+ * automated certification checks (see src/ecosystem/plugin-installer.js and
4
+ * plugin-checks.js).
5
+ */
6
+
7
+ const fs = require("fs/promises");
8
+ const { resolveConfigPath } = require("../../../config/load-config.js");
9
+
10
+ const ICONS = { high: "❌", medium: "⚠️ ", low: "ℹ️ " };
11
+
12
+ function printFindings(findings) {
13
+ for (const f of findings) {
14
+ const where = f.line ? `${f.file}:${f.line}` : f.file;
15
+ console.log(
16
+ ` ${ICONS[f.severity] || "-"} ${f.severity}: ${f.message} (${where})`,
17
+ );
18
+ }
19
+ }
20
+
21
+ /**
22
+ * @param {string} source - Registry id, path, .tgz, .js or npm:<spec>
23
+ * @param {object} options - { stage, name, force, allowRisky, version }
24
+ * @param {object} globals - Includes the root --config option
25
+ * @param {Function} getHub - Returns a PluginHub, or null when no registry is set
26
+ */
27
+ async function installFromSource(source, options, globals, getHub) {
28
+ try {
29
+ const { installPlugin } = require("../../../ecosystem/plugin-installer.js");
30
+ const result = await installPlugin({
31
+ source,
32
+ configPath: resolveConfigPath(globals.config || ".ragrc.json"),
33
+ stage: options.stage,
34
+ name: options.name,
35
+ force: options.force,
36
+ allowRisky: options.allowRisky,
37
+ version: options.version,
38
+ hub: getHub(),
39
+ onProgress: (step) => console.log(`… ${step}`),
40
+ });
41
+
42
+ if (result.scan.findings.length > 0) {
43
+ console.log("Security scan findings:");
44
+ printFindings(result.scan.findings);
45
+ }
46
+ console.log(
47
+ `✅ Installed ${result.stage} plugin "${result.name}"` +
48
+ `${result.version ? ` ${result.version}` : ""} (${result.source}) at ${result.entry}`,
49
+ );
50
+ console.log(` Methods: ${result.methods.join(", ")}`);
51
+ if (!result.inPipeline) {
52
+ console.log(
53
+ ` To use it, add { "stage": "${result.stage}", "name": "${result.name}" } to "pipeline" in your config.`,
54
+ );
55
+ }
56
+ } catch (error) {
57
+ console.error(`❌ Install failed: ${error.message}`);
58
+ if (error.scan) printFindings(error.scan.findings);
59
+ process.exitCode = 1;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @param {string} pluginPath - Local plugin directory or .js file
65
+ * @param {object} options - { stage, json, output }
66
+ */
67
+ async function certifyLocal(pluginPath, options) {
68
+ try {
69
+ const { runPluginChecks } = require("../../../ecosystem/plugin-checks.js");
70
+ const report = await runPluginChecks(pluginPath, { stage: options.stage });
71
+
72
+ if (options.output) {
73
+ await fs.writeFile(options.output, JSON.stringify(report, null, 2));
74
+ }
75
+ if (options.json) {
76
+ console.log(JSON.stringify(report, null, 2));
77
+ } else {
78
+ console.log(`Certification checks: ${report.plugin} (${report.stage})\n`);
79
+ for (const check of report.checks) {
80
+ console.log(
81
+ `${check.label}: ${check.score}/100 (weight ${check.weight})`,
82
+ );
83
+ check.details.forEach((d) => console.log(` - ${d}`));
84
+ }
85
+ console.log(`\nScore: ${report.score}/100`);
86
+ if (report.level) {
87
+ console.log(
88
+ `Meets the automated threshold for ${report.level}` +
89
+ (report.pendingReview
90
+ ? ` (also requires ${report.pendingReview})`
91
+ : ""),
92
+ );
93
+ } else {
94
+ console.log(
95
+ "Does not meet the BASIC threshold (60, with a passing contract).",
96
+ );
97
+ }
98
+ if (report.nextLevel) {
99
+ console.log(
100
+ `Next level: ${report.nextLevel.level} at ${report.nextLevel.score}`,
101
+ );
102
+ }
103
+ if (options.output) console.log(`Report saved to ${options.output}`);
104
+ }
105
+ if (!report.level) process.exitCode = 1;
106
+ } catch (error) {
107
+ console.error(`❌ Certification checks failed: ${error.message}`);
108
+ process.exitCode = 1;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * @param {object} globals - Includes the root --config option
114
+ * @param {object} options - { json }
115
+ */
116
+ function listPlugins(globals, options) {
117
+ const {
118
+ listInstalledPlugins,
119
+ } = require("../../../ecosystem/plugin-installer.js");
120
+ const plugins = listInstalledPlugins(
121
+ resolveConfigPath(globals.config || ".ragrc.json"),
122
+ );
123
+ if (options.json) {
124
+ console.log(JSON.stringify(plugins, null, 2));
125
+ return;
126
+ }
127
+ if (plugins.length === 0) {
128
+ console.log('No plugins registered under "plugins" in the config.');
129
+ return;
130
+ }
131
+ for (const p of plugins) {
132
+ const flags = [
133
+ p.installed
134
+ ? `installed${p.version ? ` ${p.version}` : ""}`
135
+ : "local path",
136
+ p.inPipeline ? "in pipeline" : "not in pipeline",
137
+ p.exists ? null : "FILE MISSING",
138
+ ].filter(Boolean);
139
+ console.log(`${p.stage}/${p.name} ${p.entry} (${flags.join(", ")})`);
140
+ }
141
+ }
142
+
143
+ module.exports = { installFromSource, certifyLocal, listPlugins };
@@ -11,17 +11,80 @@ const hubCommands = require("./plugin-hub/hub-commands");
11
11
  const certificationCommands = require("./plugin-hub/certification-commands");
12
12
  const reviewCommands = require("./plugin-hub/review-commands");
13
13
  const { setupEventListeners } = require("./plugin-hub/event-handlers");
14
+ const {
15
+ installFromSource,
16
+ certifyLocal,
17
+ listPlugins,
18
+ } = require("./plugin-hub/local-commands");
14
19
 
15
20
  class PluginHubCLI {
16
- constructor() {
17
- this.hub = new PluginHub();
18
- this.certification = new PluginCertification();
19
- setupEventListeners(this.hub, this.certification);
21
+ /**
22
+ * @param {object} [options]
23
+ * @param {string} [options.registryUrl] - Overrides --registry / RAG_PLUGIN_REGISTRY
24
+ */
25
+ constructor(options = {}) {
26
+ this.registryUrl = options.registryUrl;
27
+ }
28
+
29
+ /**
30
+ * There is no public registry: the old default (registry.rag-pipeline.dev)
31
+ * does not resolve. Commands that talk to a registry need one configured.
32
+ */
33
+ _resolveRegistryUrl() {
34
+ const url = this.registryUrl || process.env.RAG_PLUGIN_REGISTRY;
35
+ if (!url) {
36
+ throw new Error(
37
+ "No plugin registry configured. Pass --registry <url> or set RAG_PLUGIN_REGISTRY.",
38
+ );
39
+ }
40
+ return url;
41
+ }
42
+
43
+ _init() {
44
+ if (!this._hub) {
45
+ const registryUrl = this._resolveRegistryUrl();
46
+ this._hub = new PluginHub({ registryUrl });
47
+ this._certification = new PluginCertification({ registryUrl });
48
+ setupEventListeners(this._hub, this._certification);
49
+ }
50
+ }
51
+
52
+ get hub() {
53
+ this._init();
54
+ return this._hub;
55
+ }
56
+
57
+ get certification() {
58
+ this._init();
59
+ return this._certification;
20
60
  }
21
61
 
22
62
  createCommands() {
23
63
  const program = new Command("hub");
24
- program.description("Community plugin hub operations");
64
+ program
65
+ .alias("plugin")
66
+ .description("Plugin registry operations (requires --registry <url>)")
67
+ .option(
68
+ "--registry <url>",
69
+ "Plugin registry URL (or RAG_PLUGIN_REGISTRY)",
70
+ )
71
+ .hook("preAction", (_hubCommand, actionCommand) => {
72
+ // Local commands: requirements and submit never need a registry; install
73
+ // needs one only for registry ids and checks that itself.
74
+ if (
75
+ ["requirements", "install", "submit", "list"].includes(
76
+ actionCommand.name(),
77
+ )
78
+ )
79
+ return;
80
+ this.registryUrl = this.registryUrl || program.opts().registry;
81
+ try {
82
+ this._resolveRegistryUrl();
83
+ } catch (error) {
84
+ console.error(`❌ ${error.message}`);
85
+ process.exit(1);
86
+ }
87
+ });
25
88
 
26
89
  // Search command
27
90
  program
@@ -42,21 +105,22 @@ class PluginHubCLI {
42
105
  await hubCommands.searchPlugins(this.hub, query, _options);
43
106
  });
44
107
 
45
- // Install command
46
108
  program
47
- .command("install <plugin>")
48
- .description("Install a plugin from the hub")
49
- .option("-v, --version <version>", "Plugin version", "latest")
50
- .option("--no-security-scan", "Skip security scan")
51
- .option("--require-certified", "Only install certified plugins")
52
- .option(
53
- "--sandbox-timeout <timeout>",
54
- "Sandbox timeout in ms",
55
- parseInt,
56
- 30000,
109
+ .command("install <source>")
110
+ .description(
111
+ "Install a plugin from a local path, .tgz, .js, npm:<package> or the registry",
57
112
  )
58
- .action(async (plugin, _options) => {
59
- await hubCommands.installPlugin(this.hub, plugin, _options);
113
+ .option("--stage <stage>", "loader, embedder, retriever, llm or reranker")
114
+ .option("--name <name>", "Name to register the plugin under")
115
+ .option("-v, --version <version>", "Registry version", "latest")
116
+ .option("--force", "Replace an installed plugin with the same stage/name")
117
+ .option("--allow-risky", "Install despite high-risk security findings")
118
+ .action(async (source, _options, command) => {
119
+ const globals = command.optsWithGlobals();
120
+ this.registryUrl = this.registryUrl || globals.registry;
121
+ const getHub = () =>
122
+ this.registryUrl || process.env.RAG_PLUGIN_REGISTRY ? this.hub : null;
123
+ await installFromSource(source, _options, globals, getHub);
60
124
  });
61
125
 
62
126
  // Info command
@@ -71,10 +135,10 @@ class PluginHubCLI {
71
135
  program
72
136
  .command("list")
73
137
  .alias("ls")
74
- .description("List installed plugins")
75
- .option("--format <format>", "Output format: table, json", "table")
76
- .action(async (_options) => {
77
- await hubCommands.listInstalledPlugins(this.hub, _options);
138
+ .description("List plugins registered in the config")
139
+ .option("--json", "Print as JSON")
140
+ .action(async (_options, command) => {
141
+ listPlugins(command.optsWithGlobals(), _options);
78
142
  });
79
143
 
80
144
  // Publish command
@@ -132,19 +196,14 @@ class PluginHubCLI {
132
196
  .description("Plugin certification operations");
133
197
 
134
198
  certifyCmd
135
- .command("submit <plugin>")
136
- .description("Submit plugin for certification")
137
- .option(
138
- "-l, --level <level>",
139
- "Certification level: BASIC, VERIFIED, ENTERPRISE",
140
- "BASIC",
141
- )
142
- .action(async (plugin, _options) => {
143
- await certificationCommands.submitForCertification(
144
- this.certification,
145
- plugin,
146
- _options,
147
- );
199
+ .command("submit <path>")
200
+ .alias("check")
201
+ .description("Run automated certification checks on a local plugin")
202
+ .option("--stage <stage>", "loader, embedder, retriever, llm or reranker")
203
+ .option("--json", "Print the report as JSON")
204
+ .option("--output <file>", "Save the report as JSON")
205
+ .action(async (pluginPath, _options) => {
206
+ await certifyLocal(pluginPath, _options);
148
207
  });
149
208
 
150
209
  certifyCmd
@@ -162,8 +221,9 @@ class PluginHubCLI {
162
221
  .command("requirements [level]")
163
222
  .description("Show certification requirements")
164
223
  .action(async (level) => {
224
+ // Requirements are a static list; no registry is contacted.
165
225
  await certificationCommands.showCertificationRequirements(
166
- this.certification,
226
+ new PluginCertification(),
167
227
  level,
168
228
  );
169
229
  });
@@ -1,78 +1,91 @@
1
1
  /**
2
- * Configuration checker for RAG Pipeline Diagnostics
3
- * Checks configuration file existence, validity, and schema compliance
2
+ * Configuration diagnostics: the file exists, parses, validates, and every
3
+ * pipeline entry resolves to a component.
4
4
  */
5
5
 
6
- const fs = require("fs/promises");
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+
9
+ function issue(severity, code, message, fix) {
10
+ return {
11
+ category: "configuration",
12
+ severity,
13
+ code,
14
+ message,
15
+ fix,
16
+ autoFixable: false,
17
+ };
18
+ }
7
19
 
8
- /**
9
- * Check configuration issues
10
- * @param {object} options - Doctor options (must include configPath)
11
- * @returns {Promise<Array>} Array of configuration issues
12
- */
13
20
  async function checkConfiguration(options) {
14
- const issues = [];
21
+ const { loadRagConfig } = require("../../config/load-config.js");
22
+ const { buildComponents } = require("../../config/component-factory.js");
23
+ const configPath = path.resolve(options.configPath);
15
24
 
16
- try {
17
- await fs.access(options.configPath);
18
- } catch (error) {
19
- issues.push({
20
- category: "configuration",
21
- severity: "error",
22
- code: "CONFIG_MISSING",
23
- message: `Configuration file not found: ${options.configPath}`,
24
- fix: 'Run "rag-pipeline init" to create a configuration file',
25
- autoFixable: false,
26
- });
27
- return issues;
25
+ if (!fs.existsSync(configPath)) {
26
+ return [
27
+ issue(
28
+ "error",
29
+ "CONFIG_MISSING",
30
+ `Configuration file not found: ${options.configPath}`,
31
+ 'Run "rag-pipeline init" to create a configuration file',
32
+ ),
33
+ ];
28
34
  }
29
35
 
36
+ let config;
30
37
  try {
31
- const configContent = await fs.readFile(options.configPath, "utf8");
32
- const config = JSON.parse(configContent);
38
+ config = loadRagConfig(configPath);
39
+ } catch (error) {
40
+ const invalidJson = /Invalid JSON/.test(error.message);
41
+ return [
42
+ issue(
43
+ "error",
44
+ invalidJson ? "CONFIG_INVALID_JSON" : "CONFIG_SCHEMA_ERROR",
45
+ error.message,
46
+ invalidJson
47
+ ? `Fix JSON syntax errors in ${options.configPath}`
48
+ : "Fix the reported configuration errors",
49
+ ),
50
+ ];
51
+ }
33
52
 
34
- // Check if config is empty
35
- if (Object.keys(config).length === 0 || !config.plugins) {
36
- issues.push({
37
- category: "configuration",
38
- severity: "warning",
39
- code: "CONFIG_EMPTY",
40
- message: "Configuration file is empty or missing required sections",
41
- fix: "Add plugin configurations and pipeline settings",
42
- autoFixable: false,
43
- });
53
+ const issues = [];
54
+ const stages = new Set(config.pipeline.map((entry) => entry.stage));
55
+ for (const stage of ["embedder", "retriever"]) {
56
+ if (!stages.has(stage)) {
57
+ issues.push(
58
+ issue(
59
+ "error",
60
+ "CONFIG_STAGE_MISSING",
61
+ `Pipeline has no ${stage} stage`,
62
+ `Add { "stage": "${stage}", "name": ... } to the pipeline (see rag-pipeline init)`,
63
+ ),
64
+ );
44
65
  }
66
+ }
67
+ if (!stages.has("llm")) {
68
+ issues.push(
69
+ issue(
70
+ "info",
71
+ "CONFIG_NO_LLM",
72
+ "Pipeline has no llm stage; query prints retrieved passages only",
73
+ 'Add { "stage": "llm", "name": "openai" } (or anthropic, ollama) to generate answers',
74
+ ),
75
+ );
76
+ }
45
77
 
46
- // Validate schema if available
47
- try {
48
- const {
49
- validateEnhancedRagrcSchema,
50
- } = require("../../config/enhanced-ragrc-schema.js");
51
- const validation = validateEnhancedRagrcSchema(config);
52
- if (!validation.valid && validation.errors) {
53
- validation.errors.forEach((error) => {
54
- issues.push({
55
- category: "configuration",
56
- severity: "error",
57
- code: "CONFIG_SCHEMA_ERROR",
58
- message: `Schema validation failed: ${error.instancePath} ${error.message}`,
59
- fix: "Fix configuration schema errors",
60
- autoFixable: false,
61
- });
62
- });
63
- }
64
- } catch (schemaError) {
65
- // Schema validation not available, skip
66
- }
67
- } catch (parseError) {
68
- issues.push({
69
- category: "configuration",
70
- severity: "error",
71
- code: "CONFIG_INVALID_JSON",
72
- message: "Configuration file contains invalid JSON syntax",
73
- fix: `Fix JSON syntax errors in ${options.configPath}`,
74
- autoFixable: false,
75
- });
78
+ try {
79
+ buildComponents(config, { configDir: path.dirname(configPath) });
80
+ } catch (error) {
81
+ issues.push(
82
+ issue(
83
+ "error",
84
+ "CONFIG_COMPONENT_UNKNOWN",
85
+ error.message,
86
+ 'Use a built-in name or register the plugin under "plugins"',
87
+ ),
88
+ );
76
89
  }
77
90
 
78
91
  return issues;