relaybrain 0.0.0-stage → 0.2.1

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +434 -3
  3. package/backend/coordinator/client.js +364 -0
  4. package/backend/coordinator/lockTable.js +649 -0
  5. package/backend/coordinator/server.js +179 -0
  6. package/backend/lib/centralAuth.js +62 -0
  7. package/backend/lib/centralStore.js +112 -0
  8. package/backend/lib/centralSync.js +88 -0
  9. package/backend/lib/cloneRepo.js +108 -0
  10. package/backend/lib/collisionMetrics.js +231 -0
  11. package/backend/lib/conflicts.js +35 -0
  12. package/backend/lib/dashboard.js +53 -0
  13. package/backend/lib/depGraph.js +393 -0
  14. package/backend/lib/deps.js +46 -0
  15. package/backend/lib/dirtyFiles.js +33 -0
  16. package/backend/lib/editMatchers.js +55 -0
  17. package/backend/lib/falkor/analytics.js +228 -0
  18. package/backend/lib/falkor/bus.js +16 -0
  19. package/backend/lib/falkor/central.js +281 -0
  20. package/backend/lib/falkor/client.js +165 -0
  21. package/backend/lib/falkor/config.js +73 -0
  22. package/backend/lib/falkor/coord.js +299 -0
  23. package/backend/lib/falkor/cursor.js +58 -0
  24. package/backend/lib/falkor/demo.js +221 -0
  25. package/backend/lib/falkor/fixtures.js +85 -0
  26. package/backend/lib/falkor/index.js +146 -0
  27. package/backend/lib/falkor/ingest.js +233 -0
  28. package/backend/lib/falkor/intents.js +32 -0
  29. package/backend/lib/falkor/keys.js +102 -0
  30. package/backend/lib/falkor/migrations.js +56 -0
  31. package/backend/lib/falkor/project.js +307 -0
  32. package/backend/lib/falkor/query.js +344 -0
  33. package/backend/lib/falkor/redact.js +30 -0
  34. package/backend/lib/falkor/routes.js +125 -0
  35. package/backend/lib/falkor/schema.js +108 -0
  36. package/backend/lib/falkor/write.js +281 -0
  37. package/backend/lib/githubLogin.js +108 -0
  38. package/backend/lib/githubOAuth.js +42 -0
  39. package/backend/lib/githubRepo.js +117 -0
  40. package/backend/lib/harvest.js +85 -0
  41. package/backend/lib/httpServer.js +27 -0
  42. package/backend/lib/installAgentRelay.js +235 -0
  43. package/backend/lib/installHooks.js +309 -0
  44. package/backend/lib/lamport.js +14 -0
  45. package/backend/lib/localBrowse.js +109 -0
  46. package/backend/lib/memorySync.js +63 -0
  47. package/backend/lib/notices.js +150 -0
  48. package/backend/lib/ot.js +74 -0
  49. package/backend/lib/patches.js +192 -0
  50. package/backend/lib/paths.js +85 -0
  51. package/backend/lib/peerClash.js +64 -0
  52. package/backend/lib/presence.js +35 -0
  53. package/backend/lib/relayOsBlock.js +84 -0
  54. package/backend/lib/repoSnapshot.js +125 -0
  55. package/backend/lib/room.js +638 -0
  56. package/backend/lib/roomAsk.js +148 -0
  57. package/backend/lib/roomAuth.js +215 -0
  58. package/backend/lib/roomControl.js +226 -0
  59. package/backend/lib/roomLocks.js +201 -0
  60. package/backend/lib/roomSignal.js +265 -0
  61. package/backend/lib/roomSync.js +194 -0
  62. package/backend/lib/seed.js +107 -0
  63. package/backend/lib/sseHub.js +40 -0
  64. package/backend/lib/store.js +79 -0
  65. package/backend/lib/supergraph.js +46 -0
  66. package/backend/lib/transcripts/discover.js +437 -0
  67. package/backend/lib/transcripts/parse.js +558 -0
  68. package/backend/lib/transcripts/sessionId.js +36 -0
  69. package/backend/lib/transcripts/sync.js +291 -0
  70. package/backend/lib/transcripts/util.js +299 -0
  71. package/backend/lib/treeSitterImports.js +219 -0
  72. package/backend/lib/watchWorkspace.js +42 -0
  73. package/backend/mcp/README.md +193 -0
  74. package/backend/mcp/server.js +42 -0
  75. package/backend/mcp/tools.js +484 -0
  76. package/backend/server.js +3894 -0
  77. package/cli/graph.js +286 -0
  78. package/cli/relay.js +475 -0
  79. package/docker-compose.falkor.yml +17 -0
  80. package/docs/MEMORY-GRAPH.md +314 -0
  81. package/hooks/relay-antigravity-post-tool.js +2 -0
  82. package/hooks/relay-antigravity-pre-read.js +2 -0
  83. package/hooks/relay-antigravity-pre-tool.js +2 -0
  84. package/hooks/relay-antigravity-stop.js +2 -0
  85. package/hooks/relay-claude-post-tool.js +2 -0
  86. package/hooks/relay-claude-pre-read.js +2 -0
  87. package/hooks/relay-claude-pre-tool.js +2 -0
  88. package/hooks/relay-claude-stop.js +2 -0
  89. package/hooks/relay-codex-post-tool.js +2 -0
  90. package/hooks/relay-codex-pre-read.js +2 -0
  91. package/hooks/relay-codex-pre-tool.js +2 -0
  92. package/hooks/relay-codex-stop.js +2 -0
  93. package/hooks/relay-copilot-post-tool.js +2 -0
  94. package/hooks/relay-copilot-pre-read.js +2 -0
  95. package/hooks/relay-copilot-pre-tool.js +2 -0
  96. package/hooks/relay-copilot-stop.js +2 -0
  97. package/hooks/relay-cursor-post-tool.js +2 -0
  98. package/hooks/relay-cursor-pre-read.js +2 -0
  99. package/hooks/relay-cursor-pre-tool.js +2 -0
  100. package/hooks/relay-cursor-stop.js +2 -0
  101. package/hooks/relay-hook-lib.js +621 -0
  102. package/mission-control/out/404.html +1 -0
  103. package/mission-control/out/__next.__PAGE__.txt +26 -0
  104. package/mission-control/out/__next._full.txt +21 -0
  105. package/mission-control/out/__next._tree.txt +4 -0
  106. package/mission-control/out/_next/static/BuhfCxd0WQE65yIzZJCOM/_buildManifest.js +11 -0
  107. package/mission-control/out/_next/static/BuhfCxd0WQE65yIzZJCOM/_clientMiddlewareManifest.js +1 -0
  108. package/mission-control/out/_next/static/BuhfCxd0WQE65yIzZJCOM/_ssgManifest.js +1 -0
  109. package/mission-control/out/_next/static/chunks/0-3l04fnym95-.js +22 -0
  110. package/mission-control/out/_next/static/chunks/064ntycm-fx5v.js +1 -0
  111. package/mission-control/out/_next/static/chunks/0cz1d0mv5g_q7.js +1 -0
  112. package/mission-control/out/_next/static/chunks/0ehjiuuxbbhq9.js +1 -0
  113. package/mission-control/out/_next/static/chunks/1-dy52ls2ntb_.css +4 -0
  114. package/mission-control/out/_next/static/chunks/1q0cmt0kqufow.js +2 -0
  115. package/mission-control/out/_next/static/chunks/1ya1u0izct4f6.js +1 -0
  116. package/mission-control/out/_next/static/chunks/2n66mlnb34vjq.js +1 -0
  117. package/mission-control/out/_next/static/chunks/2xar70jzx8f1y.css +1 -0
  118. package/mission-control/out/_next/static/chunks/31cgqj_-l15yt.js +31 -0
  119. package/mission-control/out/_next/static/chunks/31qgr-p-oulpb.js +1 -0
  120. package/mission-control/out/_next/static/chunks/3vq9c4hivvgn_.js +1 -0
  121. package/mission-control/out/_next/static/chunks/turbopack-3hsllzpnzz12p.js +1 -0
  122. package/mission-control/out/_next/static/media/icon.27otmh_fv3buh.svg +4 -0
  123. package/mission-control/out/_not-found/__next._full.txt +18 -0
  124. package/mission-control/out/_not-found/__next._not-found/__PAGE__.txt +24 -0
  125. package/mission-control/out/_not-found/__next._tree.txt +3 -0
  126. package/mission-control/out/_not-found.html +1 -0
  127. package/mission-control/out/_not-found.txt +18 -0
  128. package/mission-control/out/agents/antigravity.png +0 -0
  129. package/mission-control/out/agents/claude.png +0 -0
  130. package/mission-control/out/agents/codex.png +0 -0
  131. package/mission-control/out/agents/copilot.png +0 -0
  132. package/mission-control/out/agents/cursor.png +0 -0
  133. package/mission-control/out/icon.svg +4 -0
  134. package/mission-control/out/index.html +1 -0
  135. package/mission-control/out/index.txt +21 -0
  136. package/mission-control/out/logo.png +0 -0
  137. package/mission-control/out/logo_transparent.png +0 -0
  138. package/package.json +76 -6
  139. package/scripts/falkor-rebuild.js +38 -0
  140. package/scripts/falkor-seed-demo.js +29 -0
package/cli/graph.js ADDED
@@ -0,0 +1,286 @@
1
+ // `relay graph …`: run, enable and inspect the FalkorDB memory graph without env vars.
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const { spawn, spawnSync } = require("child_process");
5
+ const { readConfig, saveFileConfig, clearFileConfig, loadFileConfig, configPath, validUrl } = require("../backend/lib/falkor/config");
6
+
7
+ const root = path.join(__dirname, "..");
8
+ const compose = path.join(root, "docker-compose.falkor.yml");
9
+ const DEFAULT_URL = "falkor://127.0.0.1:6379";
10
+
11
+ function api() {
12
+ return `http://127.0.0.1:${Number(process.env.RELAY_PORT || 3001)}`;
13
+ }
14
+
15
+ function mask(url) {
16
+ return url ? url.replace(/\/\/[^@/]*@/, "//***@") : url;
17
+ }
18
+
19
+ async function getJson(url, init) {
20
+ const r = await fetch(url, { ...init, signal: AbortSignal.timeout(6000) });
21
+ return r.json();
22
+ }
23
+
24
+ function help() {
25
+ console.log(`relay graph: the FalkorDB memory graph
26
+
27
+ relay graph up Start FalkorDB in Docker (docker-compose.falkor.yml)
28
+ relay graph enable [url] Save the FalkorDB URL (default ${DEFAULT_URL}) and switch a running relay on
29
+ relay graph disable Turn the graph off (takes effect on the next relay serve)
30
+ relay graph status Config, connection and per-project ingest state
31
+ relay graph stats [id|all] Node/edge counts per label, memory usage, and turn time range
32
+ relay graph rebuild [id|all] Rebuild a project's graph from its relay history (default: all)
33
+ relay graph seed [id] Seed a realistic 5-agent coordination graph for live demos
34
+ `);
35
+ }
36
+
37
+ function up() {
38
+ const docker = spawnSync("docker", ["info", "--format", "{{.ServerVersion}}"], { encoding: "utf8", windowsHide: true });
39
+ if (docker.error || docker.status !== 0) {
40
+ console.error("Docker is not running. Start Docker Desktop (or the docker daemon), then run `relay graph up` again.");
41
+ console.error(`Or run FalkorDB yourself and point relay at it: relay graph enable falkor://host:port`);
42
+ process.exitCode = 1;
43
+ return;
44
+ }
45
+ const r = spawnSync("docker", ["compose", "-f", compose, "up", "-d"], { stdio: "inherit", windowsHide: true });
46
+ if (r.status !== 0) {
47
+ process.exitCode = r.status || 1;
48
+ return;
49
+ }
50
+ console.log(`\nFalkorDB is up on ${DEFAULT_URL} (browser UI: http://localhost:3010).`);
51
+ console.log("Next: relay graph enable");
52
+ }
53
+
54
+ async function probe(url) {
55
+ const { FalkorDB } = require("falkordb");
56
+ const db = await Promise.race([
57
+ FalkorDB.connect({ url, socket: { connectTimeout: 3000, reconnectStrategy: false } }),
58
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timed out")), 4000)),
59
+ ]);
60
+ try {
61
+ db.on?.("error", () => undefined);
62
+ return await db.list();
63
+ } finally {
64
+ await db.close().catch(() => undefined);
65
+ }
66
+ }
67
+
68
+ async function enable(url) {
69
+ const target = (url || "").trim() || DEFAULT_URL;
70
+ if (!validUrl(target)) {
71
+ console.error(`Not a FalkorDB URL: ${target} (expected falkor://[user:pass@]host:port)`);
72
+ process.exitCode = 1;
73
+ return;
74
+ }
75
+ try {
76
+ const graphs = await probe(target);
77
+ console.log(`Connected to ${mask(target)} (${graphs.length} graph(s)).`);
78
+ } catch (err) {
79
+ console.error(`Cannot reach ${mask(target)}: ${err.message || err}`);
80
+ console.error("Start it with `relay graph up`, or pass the right URL. Saving anyway.");
81
+ }
82
+ let running = false;
83
+ try {
84
+ const body = await getJson(`${api()}/api/graph/enable`, {
85
+ method: "POST",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify({ url: target }),
88
+ });
89
+ running = true;
90
+ if (!body.ok) console.error(`relay serve refused: ${body.reason}`);
91
+ else console.log(body.note || body.warning || "relay serve: memory graph is on.");
92
+ } catch {
93
+ saveFileConfig({ ...(loadFileConfig() || {}), url: target });
94
+ }
95
+ console.log(`Saved to ${configPath()}.`);
96
+ if (!running) console.log("Start relay (`relay serve`) and it will use the graph.");
97
+ const env = readConfig();
98
+ if (env.source === "env") console.log(`Note: RELAY_FALKOR_URL is set in this shell and overrides the saved URL.`);
99
+ }
100
+
101
+ function disable() {
102
+ const cfg = loadFileConfig();
103
+ if (!cfg) {
104
+ console.log("No saved graph config. (If RELAY_FALKOR_URL is set in your shell, unset it.)");
105
+ return;
106
+ }
107
+ clearFileConfig();
108
+ console.log(`Removed ${configPath()}. Restart relay serve to stop using the graph.`);
109
+ }
110
+
111
+ async function status() {
112
+ const cfg = readConfig();
113
+ console.log(`config: ${cfg.url ? `${mask(cfg.url)} (from ${cfg.source})` : "none — run `relay graph enable`"}`);
114
+ console.log(`file: ${configPath()}${fs.existsSync(configPath()) ? "" : " (not present)"}`);
115
+ let health = null;
116
+ try {
117
+ health = await getJson(`${api()}/api/graph/health`);
118
+ } catch {
119
+ console.log("relay: not running on " + api());
120
+ }
121
+ if (health) {
122
+ const b = health.breaker;
123
+ // Breaker states, in plain words: "closed" is the healthy one.
124
+ const conn = !b ? "" : b.state === "closed" ? ", connected" : b.state === "open" ? ", FalkorDB unreachable (retrying)" : ", reconnecting";
125
+ console.log(`relay: graph ${health.running ? "running" : "off"}${conn}${b?.lastError && b.state !== "closed" ? ` (${b.lastError})` : ""}`);
126
+ const projects = Object.entries(health.projects || {});
127
+ if (!projects.length) console.log(" no project ingested yet in this process");
128
+ for (const [id, p] of projects) {
129
+ const when = p.lastIngestAt ? new Date(p.lastIngestAt).toLocaleString() : "never";
130
+ console.log(` ${id}: last ingest ${when}${p.needsRebuild ? ", NEEDS REBUILD (relay graph rebuild)" : ""}${p.lastError ? `, error: ${p.lastError}` : ""}`);
131
+ }
132
+ }
133
+ if (cfg.url) {
134
+ try {
135
+ const graphs = await probe(cfg.url);
136
+ console.log(`db: reachable, ${graphs.length} graph(s): ${graphs.slice(0, 8).join(", ")}${graphs.length > 8 ? ", …" : ""}`);
137
+ } catch (err) {
138
+ console.log(`db: unreachable (${err.message || err}). Try \`relay graph up\`.`);
139
+ process.exitCode = 1;
140
+ }
141
+ }
142
+ }
143
+
144
+ function rebuild(target) {
145
+ const cfg = readConfig();
146
+ if (!cfg.url) {
147
+ console.error("No FalkorDB configured. Run `relay graph enable` first.");
148
+ process.exitCode = 1;
149
+ return Promise.resolve();
150
+ }
151
+ return new Promise((resolve) => {
152
+ const child = spawn(process.execPath, [path.join(root, "scripts", "falkor-rebuild.js"), target || "all"], {
153
+ stdio: "inherit",
154
+ env: { ...process.env, RELAY_FALKOR_URL: cfg.url },
155
+ windowsHide: true,
156
+ });
157
+ child.on("exit", (code) => {
158
+ process.exitCode = code || 0;
159
+ resolve();
160
+ });
161
+ });
162
+ }
163
+
164
+ async function stats(target) {
165
+ const cfg = readConfig();
166
+ if (!cfg.url) {
167
+ console.error("No FalkorDB configured. Run `relay graph enable` first.");
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ const { FalkorClient } = require("../backend/lib/falkor/client");
172
+ const { loadRegistry } = require("../backend/lib/store");
173
+ const client = new FalkorClient(cfg);
174
+ try {
175
+ const db = await client.connection();
176
+ const regProjects = loadRegistry().projects || [];
177
+ let selected = target && target !== "all"
178
+ ? regProjects.filter((p) => p.id === target || p.name === target)
179
+ : [...regProjects];
180
+ if (!selected.length && target && target !== "all") {
181
+ selected = [{ id: target, name: target }];
182
+ }
183
+ if (!selected.length) {
184
+ const prefix = cfg.prefix || "relay_";
185
+ const allGraphs = (await db.list()).filter((g) => g.startsWith(prefix));
186
+ selected = allGraphs.map((g) => ({ id: g.slice(prefix.length), name: g.slice(prefix.length) }));
187
+ }
188
+ if (!selected.length) {
189
+ console.log("No matching registered projects or FalkorDB graphs.");
190
+ return;
191
+ }
192
+ for (const p of selected) {
193
+ const gName = client.graphName(p.id);
194
+ console.log(`\n${p.name} (${p.id}) -> graph ${gName}`);
195
+ try {
196
+ const nodes = await client.read(p.id, "MATCH (n) RETURN labels(n)[0] AS label, count(n) AS count ORDER BY count DESC");
197
+ const edges = await client.read(p.id, "MATCH ()-[r]->() RETURN type(r) AS type, count(r) AS count ORDER BY count DESC");
198
+ const span = await client.read(
199
+ p.id,
200
+ "MATCH (t:Turn) RETURN min(t.ts) AS oldest, max(t.ts) AS newest, count(t.text) AS withText, count(t) AS total"
201
+ );
202
+ const meta = await client.read(
203
+ p.id,
204
+ "MATCH (m:Meta {key:'meta'}) RETURN m.schemaVersion AS s, m.ingestVersion AS i, coalesce(m.migrations, []) AS migs, m.lastIngestAt AS lastIngestAt"
205
+ );
206
+ let memBytes = null;
207
+ try {
208
+ const g = db.selectGraph(gName);
209
+ if (typeof g.memoryUsage === "function") {
210
+ const mem = await g.memoryUsage();
211
+ memBytes = Array.isArray(mem) ? mem[1] : mem?.total_graph_sz_mb || mem;
212
+ }
213
+ } catch {
214
+ /* GRAPH.MEMORY USAGE optional on some server builds */
215
+ }
216
+ const nodeStr = nodes.map((r) => `${r.label}:${r.count}`).join(" ") || "empty";
217
+ const edgeStr = edges.map((r) => `${r.type}:${r.count}`).join(" ") || "none";
218
+ console.log(` nodes: ${nodeStr}`);
219
+ console.log(` edges: ${edgeStr}`);
220
+ if (span[0]?.total) {
221
+ const oldest = span[0].oldest ? new Date(Number(span[0].oldest)).toISOString() : "n/a";
222
+ const newest = span[0].newest ? new Date(Number(span[0].newest)).toISOString() : "n/a";
223
+ console.log(` turns: ${span[0].withText}/${span[0].total} with text (${oldest} .. ${newest})`);
224
+ }
225
+ if (meta[0]) {
226
+ console.log(` schema: v${meta[0].s} (ingest v${meta[0].i}), migrations: ${(meta[0].migs || []).join(", ") || "none"}`);
227
+ }
228
+ if (memBytes != null) {
229
+ console.log(` memory: ${typeof memBytes === "number" ? `${(memBytes / 1024).toFixed(1)} KB` : JSON.stringify(memBytes)}`);
230
+ }
231
+ } catch (err) {
232
+ console.log(` (no graph or unreachable: ${err.message || err})`);
233
+ }
234
+ }
235
+ } catch (err) {
236
+ console.error(`Cannot connect to FalkorDB at ${mask(cfg.url)}: ${err.message || err}`);
237
+ process.exitCode = 1;
238
+ } finally {
239
+ await client.close();
240
+ }
241
+ }
242
+
243
+ function seed(target) {
244
+ const cfg = readConfig();
245
+ if (!cfg.url) {
246
+ console.error("No FalkorDB configured. Run `relay graph enable` first.");
247
+ process.exitCode = 1;
248
+ return Promise.resolve();
249
+ }
250
+ return new Promise((resolve) => {
251
+ const args = [path.join(root, "scripts", "falkor-seed-demo.js")];
252
+ if (target) args.push(target);
253
+ const child = spawn(process.execPath, args, {
254
+ stdio: "inherit",
255
+ env: { ...process.env, RELAY_FALKOR_URL: cfg.url },
256
+ windowsHide: true,
257
+ });
258
+ child.on("exit", (code) => {
259
+ process.exitCode = code || 0;
260
+ resolve();
261
+ });
262
+ });
263
+ }
264
+
265
+ async function graph(args) {
266
+ const [sub, arg] = args;
267
+ // `relay graph seed --help` must not seed a project called "--help".
268
+ if (args.some((a) => a === "--help" || a === "-h")) return help();
269
+ if (arg && arg.startsWith("-")) {
270
+ console.error(`unknown option ${arg}`);
271
+ help();
272
+ process.exitCode = 1;
273
+ return;
274
+ }
275
+ if (sub === "up") return up();
276
+ if (sub === "enable") return enable(arg);
277
+ if (sub === "disable") return disable();
278
+ if (sub === "status") return status();
279
+ if (sub === "stats") return stats(arg);
280
+ if (sub === "rebuild") return rebuild(arg);
281
+ if (sub === "seed") return seed(arg);
282
+ help();
283
+ if (sub && sub !== "help") process.exitCode = 1;
284
+ }
285
+
286
+ module.exports = { graph };