ruflo 3.57.0 → 3.57.2
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
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
[](https://github.com/ruvnet/claude-flow)
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<a href="
|
|
13
|
+
<a href="docs/harness-origins.md"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/e69e4b95aa08d65ea0f7715489bbf8e62a83a0d2/docs/assets/readme/badges/first-commit.svg" width="300" height="76" alt="Claude Flow lineage: first commit June 2, 2025 by rUv; inspect the evidence"></a>
|
|
14
|
+
<a href="data/npm-downloads.latest.json"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/275aff65a198af758923669acff36f03d1f41623/docs/assets/readme/badges/downloads.svg" width="300" height="76" alt="Illustrative download counter starts at 98,000,000 and adds varied amounts each second in a 30 second loop. Verified dataset: 97,022,120 download events."></a>
|
|
14
15
|
<br>
|
|
15
|
-
<a href="https://github.com/ruvnet/claude-flow"><img src="docs/assets/readme/badges/claude.svg
|
|
16
|
-
<a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="docs/assets/readme/badges/codex.svg
|
|
16
|
+
<a href="https://github.com/ruvnet/claude-flow"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/claude.svg" width="300" height="76" alt="Claude Code plugin"></a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/codex.svg" width="300" height="76" alt="Codex plugin"></a>
|
|
17
18
|
</p>
|
|
18
19
|
|
|
19
20
|
</div>
|
|
@@ -109,6 +110,14 @@ User → Ruflo (CLI/MCP) → Router → Swarm → Agents → Memory → LLM prov
|
|
|
109
110
|
|
|
110
111
|
> **New to Ruflo?** You don't need to learn 314 MCP tools or 26 CLI commands. After `init`, just use Claude Code normally — the hooks system automatically routes tasks, learns from successful patterns, and coordinates agents in the background.
|
|
111
112
|
|
|
113
|
+
### An origin you can inspect
|
|
114
|
+
|
|
115
|
+
[](docs/harness-origins.md)
|
|
116
|
+
|
|
117
|
+
**Built by [Reuven Cohen (rUv)](https://github.com/ruvnet), originally as Claude Flow.** The [first commit, June 2, 2025](https://github.com/ruvnet/ruflo/commit/7c1f9402d70e7af56c01a879073cbac92564dc4e), describes a code orchestration layer for recursive agent cycles: Claude writes, edits, tests and improves. The [June 10 source snapshot](https://github.com/ruvnet/ruflo/tree/dba545f012ea92a2375a26332bbf3797495e93c9) adds orchestration, task scheduling, memory and terminal modules.
|
|
118
|
+
|
|
119
|
+
A harness is the workbench around the model: it organizes tasks, carries context between steps and helps check results. **This establishes Ruflo's lineage, not a universal first across every Claude Code tool.** [See the illustrated explainer, exact commits and earlier ecosystem context](docs/harness-origins.md).
|
|
120
|
+
|
|
112
121
|
<details>
|
|
113
122
|
<summary><strong>📖 Background — where the name comes from</strong></summary>
|
|
114
123
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruflo",
|
|
3
|
-
"version": "3.57.
|
|
3
|
+
"version": "3.57.2",
|
|
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.
|
|
43
|
+
"@claude-flow/cli": "3.57.2"
|
|
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
|
-
|
|
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
|
{
|