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.
- package/LICENSE +21 -0
- package/README.md +434 -3
- package/backend/coordinator/client.js +364 -0
- package/backend/coordinator/lockTable.js +649 -0
- package/backend/coordinator/server.js +179 -0
- package/backend/lib/centralAuth.js +62 -0
- package/backend/lib/centralStore.js +112 -0
- package/backend/lib/centralSync.js +88 -0
- package/backend/lib/cloneRepo.js +108 -0
- package/backend/lib/collisionMetrics.js +231 -0
- package/backend/lib/conflicts.js +35 -0
- package/backend/lib/dashboard.js +53 -0
- package/backend/lib/depGraph.js +393 -0
- package/backend/lib/deps.js +46 -0
- package/backend/lib/dirtyFiles.js +33 -0
- package/backend/lib/editMatchers.js +55 -0
- package/backend/lib/falkor/analytics.js +228 -0
- package/backend/lib/falkor/bus.js +16 -0
- package/backend/lib/falkor/central.js +281 -0
- package/backend/lib/falkor/client.js +165 -0
- package/backend/lib/falkor/config.js +73 -0
- package/backend/lib/falkor/coord.js +299 -0
- package/backend/lib/falkor/cursor.js +58 -0
- package/backend/lib/falkor/demo.js +221 -0
- package/backend/lib/falkor/fixtures.js +85 -0
- package/backend/lib/falkor/index.js +146 -0
- package/backend/lib/falkor/ingest.js +233 -0
- package/backend/lib/falkor/intents.js +32 -0
- package/backend/lib/falkor/keys.js +102 -0
- package/backend/lib/falkor/migrations.js +56 -0
- package/backend/lib/falkor/project.js +307 -0
- package/backend/lib/falkor/query.js +344 -0
- package/backend/lib/falkor/redact.js +30 -0
- package/backend/lib/falkor/routes.js +125 -0
- package/backend/lib/falkor/schema.js +108 -0
- package/backend/lib/falkor/write.js +281 -0
- package/backend/lib/githubLogin.js +108 -0
- package/backend/lib/githubOAuth.js +42 -0
- package/backend/lib/githubRepo.js +117 -0
- package/backend/lib/harvest.js +85 -0
- package/backend/lib/httpServer.js +27 -0
- package/backend/lib/installAgentRelay.js +235 -0
- package/backend/lib/installHooks.js +309 -0
- package/backend/lib/lamport.js +14 -0
- package/backend/lib/localBrowse.js +109 -0
- package/backend/lib/memorySync.js +63 -0
- package/backend/lib/notices.js +150 -0
- package/backend/lib/ot.js +74 -0
- package/backend/lib/patches.js +192 -0
- package/backend/lib/paths.js +85 -0
- package/backend/lib/peerClash.js +64 -0
- package/backend/lib/presence.js +35 -0
- package/backend/lib/relayOsBlock.js +84 -0
- package/backend/lib/repoSnapshot.js +125 -0
- package/backend/lib/room.js +638 -0
- package/backend/lib/roomAsk.js +148 -0
- package/backend/lib/roomAuth.js +215 -0
- package/backend/lib/roomControl.js +226 -0
- package/backend/lib/roomLocks.js +201 -0
- package/backend/lib/roomSignal.js +265 -0
- package/backend/lib/roomSync.js +194 -0
- package/backend/lib/seed.js +107 -0
- package/backend/lib/sseHub.js +40 -0
- package/backend/lib/store.js +79 -0
- package/backend/lib/supergraph.js +46 -0
- package/backend/lib/transcripts/discover.js +437 -0
- package/backend/lib/transcripts/parse.js +558 -0
- package/backend/lib/transcripts/sessionId.js +36 -0
- package/backend/lib/transcripts/sync.js +291 -0
- package/backend/lib/transcripts/util.js +299 -0
- package/backend/lib/treeSitterImports.js +219 -0
- package/backend/lib/watchWorkspace.js +42 -0
- package/backend/mcp/README.md +193 -0
- package/backend/mcp/server.js +42 -0
- package/backend/mcp/tools.js +484 -0
- package/backend/server.js +3894 -0
- package/cli/graph.js +286 -0
- package/cli/relay.js +475 -0
- package/docker-compose.falkor.yml +17 -0
- package/docs/MEMORY-GRAPH.md +314 -0
- package/hooks/relay-antigravity-post-tool.js +2 -0
- package/hooks/relay-antigravity-pre-read.js +2 -0
- package/hooks/relay-antigravity-pre-tool.js +2 -0
- package/hooks/relay-antigravity-stop.js +2 -0
- package/hooks/relay-claude-post-tool.js +2 -0
- package/hooks/relay-claude-pre-read.js +2 -0
- package/hooks/relay-claude-pre-tool.js +2 -0
- package/hooks/relay-claude-stop.js +2 -0
- package/hooks/relay-codex-post-tool.js +2 -0
- package/hooks/relay-codex-pre-read.js +2 -0
- package/hooks/relay-codex-pre-tool.js +2 -0
- package/hooks/relay-codex-stop.js +2 -0
- package/hooks/relay-copilot-post-tool.js +2 -0
- package/hooks/relay-copilot-pre-read.js +2 -0
- package/hooks/relay-copilot-pre-tool.js +2 -0
- package/hooks/relay-copilot-stop.js +2 -0
- package/hooks/relay-cursor-post-tool.js +2 -0
- package/hooks/relay-cursor-pre-read.js +2 -0
- package/hooks/relay-cursor-pre-tool.js +2 -0
- package/hooks/relay-cursor-stop.js +2 -0
- package/hooks/relay-hook-lib.js +621 -0
- package/mission-control/out/404.html +1 -0
- package/mission-control/out/__next.__PAGE__.txt +26 -0
- package/mission-control/out/__next._full.txt +21 -0
- package/mission-control/out/__next._tree.txt +4 -0
- package/mission-control/out/_next/static/BuhfCxd0WQE65yIzZJCOM/_buildManifest.js +11 -0
- package/mission-control/out/_next/static/BuhfCxd0WQE65yIzZJCOM/_clientMiddlewareManifest.js +1 -0
- package/mission-control/out/_next/static/BuhfCxd0WQE65yIzZJCOM/_ssgManifest.js +1 -0
- package/mission-control/out/_next/static/chunks/0-3l04fnym95-.js +22 -0
- package/mission-control/out/_next/static/chunks/064ntycm-fx5v.js +1 -0
- package/mission-control/out/_next/static/chunks/0cz1d0mv5g_q7.js +1 -0
- package/mission-control/out/_next/static/chunks/0ehjiuuxbbhq9.js +1 -0
- package/mission-control/out/_next/static/chunks/1-dy52ls2ntb_.css +4 -0
- package/mission-control/out/_next/static/chunks/1q0cmt0kqufow.js +2 -0
- package/mission-control/out/_next/static/chunks/1ya1u0izct4f6.js +1 -0
- package/mission-control/out/_next/static/chunks/2n66mlnb34vjq.js +1 -0
- package/mission-control/out/_next/static/chunks/2xar70jzx8f1y.css +1 -0
- package/mission-control/out/_next/static/chunks/31cgqj_-l15yt.js +31 -0
- package/mission-control/out/_next/static/chunks/31qgr-p-oulpb.js +1 -0
- package/mission-control/out/_next/static/chunks/3vq9c4hivvgn_.js +1 -0
- package/mission-control/out/_next/static/chunks/turbopack-3hsllzpnzz12p.js +1 -0
- package/mission-control/out/_next/static/media/icon.27otmh_fv3buh.svg +4 -0
- package/mission-control/out/_not-found/__next._full.txt +18 -0
- package/mission-control/out/_not-found/__next._not-found/__PAGE__.txt +24 -0
- package/mission-control/out/_not-found/__next._tree.txt +3 -0
- package/mission-control/out/_not-found.html +1 -0
- package/mission-control/out/_not-found.txt +18 -0
- package/mission-control/out/agents/antigravity.png +0 -0
- package/mission-control/out/agents/claude.png +0 -0
- package/mission-control/out/agents/codex.png +0 -0
- package/mission-control/out/agents/copilot.png +0 -0
- package/mission-control/out/agents/cursor.png +0 -0
- package/mission-control/out/icon.svg +4 -0
- package/mission-control/out/index.html +1 -0
- package/mission-control/out/index.txt +21 -0
- package/mission-control/out/logo.png +0 -0
- package/mission-control/out/logo_transparent.png +0 -0
- package/package.json +76 -6
- package/scripts/falkor-rebuild.js +38 -0
- package/scripts/falkor-seed-demo.js +29 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const { loadRegistry, saveRegistry, saveMemory, loadMemory, id } = require("./store");
|
|
5
|
+
|
|
6
|
+
function avatar(seed, size = 80) {
|
|
7
|
+
const hash = crypto.createHash("md5").update(seed).digest("hex");
|
|
8
|
+
return `https://www.gravatar.com/avatar/${hash}?d=identicon&s=${size}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function initials(name) {
|
|
12
|
+
const parts = String(name).replace(/[./]/g, " ").trim().split(/\s+/);
|
|
13
|
+
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
|
|
14
|
+
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function agent(label, status, extra = {}) {
|
|
18
|
+
return {
|
|
19
|
+
id: label.toLowerCase().replace(/\s+/g, "-"),
|
|
20
|
+
label,
|
|
21
|
+
status,
|
|
22
|
+
lastActiveAt: Date.now() - (extra.agoMs || 0),
|
|
23
|
+
sessionId: extra.sessionId,
|
|
24
|
+
ownerId: extra.ownerId,
|
|
25
|
+
ownerLogin: extra.ownerLogin,
|
|
26
|
+
...extra,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function emptyMemory() {
|
|
31
|
+
return {
|
|
32
|
+
stats: { events: 0, agents: 0, patches: 0 },
|
|
33
|
+
agents: [
|
|
34
|
+
agent("Claude Code", "idle"),
|
|
35
|
+
agent("Cursor", "idle"),
|
|
36
|
+
agent("Codex", "idle"),
|
|
37
|
+
agent("Copilot", "idle"),
|
|
38
|
+
agent("Antigravity", "idle"),
|
|
39
|
+
],
|
|
40
|
+
collaborators: [],
|
|
41
|
+
activity: [],
|
|
42
|
+
history: [],
|
|
43
|
+
timeline: [],
|
|
44
|
+
chats: [],
|
|
45
|
+
edits: [],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function looksLikeDemo(mem) {
|
|
50
|
+
if (!mem) return false;
|
|
51
|
+
if (mem.stats?.events === 186 || mem.stats?.events === 64) return true;
|
|
52
|
+
const ids = (mem.activity || []).map((a) => a.id);
|
|
53
|
+
return ids.includes("a1") || ids.includes("u1");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Demo accounts used to be seeded here with `user_<login>` ids. Real accounts
|
|
57
|
+
// come from GitHub and are `gh_<id>`, so anything still carrying the old id
|
|
58
|
+
// scheme is a placeholder — drop it, unless it is the signed-in session.
|
|
59
|
+
function dropDemoUsers() {
|
|
60
|
+
const reg = loadRegistry();
|
|
61
|
+
const users = reg.users || [];
|
|
62
|
+
let activeId = null;
|
|
63
|
+
try {
|
|
64
|
+
activeId = require("./store").loadSession?.().userId || null;
|
|
65
|
+
} catch {
|
|
66
|
+
/* no session helper: keep every account */
|
|
67
|
+
}
|
|
68
|
+
const kept = users.filter((u) => !String(u.id || "").startsWith("user_") || u.id === activeId);
|
|
69
|
+
if (kept.length !== users.length) {
|
|
70
|
+
reg.users = kept;
|
|
71
|
+
saveRegistry(reg);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function seedIfEmpty() {
|
|
76
|
+
dropDemoUsers();
|
|
77
|
+
const current = loadRegistry();
|
|
78
|
+
if (current.projects?.length) {
|
|
79
|
+
for (const p of current.projects) {
|
|
80
|
+
const mem = loadMemory(p.id);
|
|
81
|
+
if (looksLikeDemo(mem) || mem.ir) saveMemory(p.id, emptyMemory());
|
|
82
|
+
}
|
|
83
|
+
return loadRegistry();
|
|
84
|
+
}
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const project = {
|
|
87
|
+
id: "proj_relay",
|
|
88
|
+
name: path.basename(cwd),
|
|
89
|
+
initials: initials(cwd),
|
|
90
|
+
color: "#f59e0b",
|
|
91
|
+
path: cwd,
|
|
92
|
+
remoteUrl: null,
|
|
93
|
+
apiKey: "rl_" + crypto.randomBytes(12).toString("hex"),
|
|
94
|
+
createdAt: Date.now(),
|
|
95
|
+
lastSyncAt: Date.now(),
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
fs.mkdirSync(path.join(cwd, ".relay"), { recursive: true });
|
|
99
|
+
} catch {
|
|
100
|
+
/* ignore */
|
|
101
|
+
}
|
|
102
|
+
saveMemory(project.id, emptyMemory());
|
|
103
|
+
saveRegistry({ users: loadRegistry().users, projects: [project] });
|
|
104
|
+
return loadRegistry();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { seedIfEmpty, avatar, initials, emptyMemory, id };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class SseHub {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.clients = new Set();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
subscribe(res, filter = {}) {
|
|
7
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
8
|
+
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
9
|
+
res.setHeader("Connection", "keep-alive");
|
|
10
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
11
|
+
res.flushHeaders?.();
|
|
12
|
+
const client = { res, filter };
|
|
13
|
+
this.clients.add(client);
|
|
14
|
+
res.write(`event: hello\ndata: ${JSON.stringify({ ok: true, ts: Date.now() })}\n\n`);
|
|
15
|
+
res.on("close", () => this.clients.delete(client));
|
|
16
|
+
return client;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
emit(event, payload) {
|
|
20
|
+
const data = `event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`;
|
|
21
|
+
for (const client of this.clients) {
|
|
22
|
+
const { workspaceId } = client.filter;
|
|
23
|
+
if (
|
|
24
|
+
event !== "notice" &&
|
|
25
|
+
workspaceId &&
|
|
26
|
+
payload.workspaceId &&
|
|
27
|
+
payload.workspaceId !== workspaceId
|
|
28
|
+
) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
client.res.write(data);
|
|
33
|
+
} catch {
|
|
34
|
+
this.clients.delete(client);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { SseHub };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const { dataDir, registryPath, sessionPath } = require("./paths");
|
|
5
|
+
|
|
6
|
+
function readJson(file, fallback) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
9
|
+
} catch {
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function writeJson(file, value) {
|
|
15
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
16
|
+
fs.writeFileSync(file, JSON.stringify(value, null, 2));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function id(prefix = "id") {
|
|
20
|
+
return `${prefix}_${crypto.randomBytes(6).toString("hex")}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function loadRegistry() {
|
|
24
|
+
return readJson(registryPath(), { projects: [], users: [] });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function saveRegistry(reg) {
|
|
28
|
+
writeJson(registryPath(), reg);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function loadSession() {
|
|
32
|
+
return readJson(sessionPath(), { userId: null });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function saveSession(session) {
|
|
36
|
+
writeJson(sessionPath(), session);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function projectDir(projectId) {
|
|
40
|
+
const dir = path.join(dataDir(), "projects", projectId);
|
|
41
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
42
|
+
return dir;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function memoryPath(projectId) {
|
|
46
|
+
return path.join(projectDir(projectId), "memory.json");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function loadMemory(projectId) {
|
|
50
|
+
return readJson(memoryPath(projectId), {
|
|
51
|
+
history: [],
|
|
52
|
+
timeline: [],
|
|
53
|
+
chats: [],
|
|
54
|
+
edits: [],
|
|
55
|
+
ir: {},
|
|
56
|
+
agents: [],
|
|
57
|
+
collaborators: [],
|
|
58
|
+
activity: [],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function saveMemory(projectId, memory) {
|
|
63
|
+
writeJson(memoryPath(projectId), memory);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
readJson,
|
|
68
|
+
writeJson,
|
|
69
|
+
id,
|
|
70
|
+
loadRegistry,
|
|
71
|
+
saveRegistry,
|
|
72
|
+
loadSession,
|
|
73
|
+
saveSession,
|
|
74
|
+
projectDir,
|
|
75
|
+
memoryPath,
|
|
76
|
+
loadMemory,
|
|
77
|
+
saveMemory,
|
|
78
|
+
dataDir,
|
|
79
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const graphs = new Map();
|
|
2
|
+
|
|
3
|
+
function unionKey(workspaceId) {
|
|
4
|
+
return workspaceId || "default";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function ingest(workspaceId, { userId, edges }) {
|
|
8
|
+
const key = unionKey(workspaceId);
|
|
9
|
+
let g = graphs.get(key);
|
|
10
|
+
if (!g) {
|
|
11
|
+
g = { users: new Map(), edges: new Map() };
|
|
12
|
+
graphs.set(key, g);
|
|
13
|
+
}
|
|
14
|
+
g.users.set(userId || "anon", edges || []);
|
|
15
|
+
g.edges = new Map();
|
|
16
|
+
for (const list of g.users.values()) {
|
|
17
|
+
for (const e of list) {
|
|
18
|
+
const from = e.from || e[0];
|
|
19
|
+
const to = e.to || e[1];
|
|
20
|
+
if (!from || !to) continue;
|
|
21
|
+
if (!g.edges.has(from)) g.edges.set(from, new Set());
|
|
22
|
+
g.edges.get(from).add(to);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return snapshot(workspaceId);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function dependents(workspaceId, file) {
|
|
29
|
+
const g = graphs.get(unionKey(workspaceId));
|
|
30
|
+
if (!g) return [];
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const [from, tos] of g.edges) {
|
|
33
|
+
if (tos.has(file)) out.push(from);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function snapshot(workspaceId) {
|
|
39
|
+
const g = graphs.get(unionKey(workspaceId));
|
|
40
|
+
if (!g) return { users: 0, edges: [] };
|
|
41
|
+
const edges = [];
|
|
42
|
+
for (const [from, tos] of g.edges) for (const to of tos) edges.push({ from, to });
|
|
43
|
+
return { users: g.users.size, edges };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { ingest, dependents, snapshot };
|