ruflo 3.57.1 → 3.58.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.
package/README.md CHANGED
@@ -240,6 +240,7 @@ This adds slash commands and agent definitions. `ruflo-core` (installed above) d
240
240
  | [**ruflo-browser**](plugins/ruflo-browser/README.md) | Automate browser testing with Playwright |
241
241
  | [**ruflo-jujutsu**](plugins/ruflo-jujutsu/README.md) | Analyze git diffs, score risk, suggest reviewers |
242
242
  | [**ruflo-docs**](plugins/ruflo-docs/README.md) | Generate and maintain documentation automatically |
243
+ | [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.10/plugins/agentic-qe-fleet) | External, maintained by [Agentic QE](https://github.com/proffesor-for-testing/agentic-qe): 11 QE agents, test generation, coverage, chaos and TDD skills, the `agentic-qe` MCP server, and a guard for AQE learning data (`/plugin install agentic-qe-fleet@ruflo`). Install it from one marketplace only, `agentic-qe` or `ruflo`: enabled from both, its mod and commands load twice |
243
244
 
244
245
  #### Security & Compliance
245
246
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruflo",
3
- "version": "3.57.1",
3
+ "version": "3.58.0",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration platform. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "bin/ruflo.js",
6
6
  "type": "module",
@@ -40,13 +40,14 @@
40
40
  "package:rvf": "bash src/scripts/package-rvf.sh"
41
41
  },
42
42
  "dependencies": {
43
- "@claude-flow/cli": "3.57.1"
43
+ "@claude-flow/cli": "3.58.0"
44
44
  },
45
45
  "optionalDependencies": {},
46
46
  "overrides": {
47
47
  "ruvector": "^0.2.27",
48
48
  "better-sqlite3": "12.9.0",
49
49
  "@ruvector/rvf-wasm": "0.1.5",
50
+ "@ruvector/rvf": "0.3.4",
50
51
  "@hono/node-server": ">=1.19.10",
51
52
  "flatted": ">=3.4.0",
52
53
  "tar": ">=7.5.11",
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mergeMcpServers } from "./mergeServers";
3
+
4
+ const base = [
5
+ {
6
+ name: "GitHub",
7
+ url: "https://api.githubcopilot.com/mcp/readonly",
8
+ headers: { Authorization: "Bearer server-token" },
9
+ },
10
+ { name: "Docs", url: "https://docs.example/mcp" },
11
+ ];
12
+
13
+ describe("mergeMcpServers", () => {
14
+ it("keeps a base server's credentials when the browser echoes it back without headers", () => {
15
+ const merged = mergeMcpServers(base, [
16
+ { name: "GitHub", url: "https://api.githubcopilot.com/mcp/readonly" },
17
+ ]);
18
+ expect(merged.find((s) => s.name === "GitHub")?.headers).toEqual({
19
+ Authorization: "Bearer server-token",
20
+ });
21
+ });
22
+
23
+ it("treats an empty headers object like no headers", () => {
24
+ const merged = mergeMcpServers(base, [
25
+ { name: "GitHub", url: "https://api.githubcopilot.com/mcp/readonly", headers: {} },
26
+ ]);
27
+ expect(merged.find((s) => s.name === "GitHub")?.headers?.Authorization).toBe(
28
+ "Bearer server-token"
29
+ );
30
+ });
31
+
32
+ it("lets request-supplied headers win", () => {
33
+ const merged = mergeMcpServers(base, [
34
+ {
35
+ name: "GitHub",
36
+ url: "https://api.githubcopilot.com/mcp/readonly",
37
+ headers: { Authorization: "Bearer user-token" },
38
+ },
39
+ ]);
40
+ expect(merged.find((s) => s.name === "GitHub")?.headers).toEqual({
41
+ Authorization: "Bearer user-token",
42
+ });
43
+ });
44
+
45
+ it("never sends a base server's credentials to a different URL", () => {
46
+ const merged = mergeMcpServers(base, [{ name: "GitHub", url: "https://evil.example/mcp" }]);
47
+ expect(merged.find((s) => s.name === "GitHub")?.headers).toBeUndefined();
48
+ });
49
+
50
+ it("adds new request-only servers and keeps unrelated base servers", () => {
51
+ const merged = mergeMcpServers(base, [{ name: "Mine", url: "https://mine.example/mcp" }]);
52
+ expect(merged.map((s) => s.name)).toEqual(["GitHub", "Docs", "Mine"]);
53
+ });
54
+ });
@@ -0,0 +1,31 @@
1
+ export type McpServerEntry = { name: string; url: string; headers?: Record<string, string> };
2
+
3
+ /**
4
+ * Merge request-provided MCP servers over the env-configured base servers, by name.
5
+ *
6
+ * The request wins, but `/api/mcp/servers` deliberately withholds base-server headers
7
+ * from the browser, so the copy the client echoes back has none. Replacing the base
8
+ * entry wholesale would drop its server-side credentials (e.g. `Authorization`), and
9
+ * every tool listing from the chat UI would then fail with 401 (#2901).
10
+ *
11
+ * So a request entry that carries no headers of its own keeps the base entry's
12
+ * headers, but only when it points at the same URL: a request must not be able to
13
+ * redirect a server-side token to another host by reusing a base server's name.
14
+ */
15
+ export function mergeMcpServers(
16
+ base: McpServerEntry[],
17
+ custom: McpServerEntry[]
18
+ ): McpServerEntry[] {
19
+ const byName = new Map<string, McpServerEntry>();
20
+ for (const s of base) byName.set(s.name, s);
21
+ for (const s of custom) {
22
+ const existing = byName.get(s.name);
23
+ const hasOwnHeaders = !!s.headers && Object.keys(s.headers).length > 0;
24
+ if (!hasOwnHeaders && existing?.headers && existing.url === s.url) {
25
+ byName.set(s.name, { ...s, headers: existing.headers });
26
+ } else {
27
+ byName.set(s.name, s);
28
+ }
29
+ }
30
+ return [...byName.values()];
31
+ }
@@ -1,4 +1,5 @@
1
1
  import { config } from "$lib/server/config";
2
+ import { mergeMcpServers } from "./mergeServers";
2
3
  import { MessageUpdateType, type MessageUpdate } from "$lib/types/MessageUpdate";
3
4
  import { getMcpServers } from "$lib/server/mcp/registry";
4
5
  import { isValidUrl } from "$lib/server/urlSafety";
@@ -98,13 +99,7 @@ export async function* runMcpFlow({
98
99
  // Invalidate cached tool list when the set of servers changes at request-time
99
100
  resetMcpToolsCache();
100
101
  // Deduplicate by server name (request takes precedence)
101
- const byName = new Map<
102
- string,
103
- { name: string; url: string; headers?: Record<string, string> }
104
- >();
105
- for (const s of servers) byName.set(s.name, s);
106
- for (const s of custom) byName.set(s.name, s);
107
- servers = [...byName.values()];
102
+ servers = mergeMcpServers(servers, custom);
108
103
  try {
109
104
  logger.debug(
110
105
  {