@timmeck/brain-core 1.0.0 → 1.1.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/.github/FUNDING.yml +1 -0
- package/.github/workflows/ci.yml +21 -0
- package/dist/cross-brain/client.d.ts +27 -0
- package/dist/cross-brain/client.js +84 -0
- package/dist/cross-brain/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/cross-brain/client.ts +94 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: timmeck
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master, main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master, main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: 20
|
|
17
|
+
cache: npm
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm run build
|
|
20
|
+
- run: npm test
|
|
21
|
+
if: ${{ hashFiles('vitest.config.*', 'src/**/*.test.ts', 'tests/**/*.test.ts') != '' }}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface BrainPeer {
|
|
2
|
+
name: string;
|
|
3
|
+
pipeName: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class CrossBrainClient {
|
|
6
|
+
private selfName;
|
|
7
|
+
private peers;
|
|
8
|
+
constructor(selfName: string, peers?: BrainPeer[]);
|
|
9
|
+
/**
|
|
10
|
+
* Query a specific peer brain by name.
|
|
11
|
+
* Returns null if the peer is not available.
|
|
12
|
+
*/
|
|
13
|
+
query(peerName: string, method: string, params?: unknown): Promise<unknown | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Broadcast a query to all available peer brains.
|
|
16
|
+
* Returns results from all peers that responded.
|
|
17
|
+
*/
|
|
18
|
+
broadcast(method: string, params?: unknown): Promise<{
|
|
19
|
+
name: string;
|
|
20
|
+
result: unknown;
|
|
21
|
+
}[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Check which peer brains are currently running.
|
|
24
|
+
*/
|
|
25
|
+
getAvailablePeers(): Promise<string[]>;
|
|
26
|
+
getPeerNames(): string[];
|
|
27
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { IpcClient } from '../ipc/client.js';
|
|
2
|
+
import { getPipeName } from '../utils/paths.js';
|
|
3
|
+
const DEFAULT_PEERS = [
|
|
4
|
+
{ name: 'brain', pipeName: getPipeName('brain') },
|
|
5
|
+
{ name: 'trading-brain', pipeName: getPipeName('trading-brain') },
|
|
6
|
+
{ name: 'marketing-brain', pipeName: getPipeName('marketing-brain') },
|
|
7
|
+
];
|
|
8
|
+
export class CrossBrainClient {
|
|
9
|
+
selfName;
|
|
10
|
+
peers;
|
|
11
|
+
constructor(selfName, peers) {
|
|
12
|
+
this.selfName = selfName;
|
|
13
|
+
this.peers = (peers ?? DEFAULT_PEERS).filter(p => p.name !== selfName);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Query a specific peer brain by name.
|
|
17
|
+
* Returns null if the peer is not available.
|
|
18
|
+
*/
|
|
19
|
+
async query(peerName, method, params) {
|
|
20
|
+
const peer = this.peers.find(p => p.name === peerName);
|
|
21
|
+
if (!peer)
|
|
22
|
+
return null;
|
|
23
|
+
const client = new IpcClient(peer.pipeName, 3000);
|
|
24
|
+
try {
|
|
25
|
+
await client.connect();
|
|
26
|
+
const result = await client.request(method, params);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
client.disconnect();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Broadcast a query to all available peer brains.
|
|
38
|
+
* Returns results from all peers that responded.
|
|
39
|
+
*/
|
|
40
|
+
async broadcast(method, params) {
|
|
41
|
+
const results = [];
|
|
42
|
+
const promises = this.peers.map(async (peer) => {
|
|
43
|
+
const client = new IpcClient(peer.pipeName, 3000);
|
|
44
|
+
try {
|
|
45
|
+
await client.connect();
|
|
46
|
+
const result = await client.request(method, params);
|
|
47
|
+
results.push({ name: peer.name, result });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Peer not available — skip
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
client.disconnect();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
await Promise.all(promises);
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check which peer brains are currently running.
|
|
61
|
+
*/
|
|
62
|
+
async getAvailablePeers() {
|
|
63
|
+
const available = [];
|
|
64
|
+
const checks = this.peers.map(async (peer) => {
|
|
65
|
+
const client = new IpcClient(peer.pipeName, 1000);
|
|
66
|
+
try {
|
|
67
|
+
await client.connect();
|
|
68
|
+
available.push(peer.name);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Not available
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
client.disconnect();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
await Promise.all(checks);
|
|
78
|
+
return available;
|
|
79
|
+
}
|
|
80
|
+
getPeerNames() {
|
|
81
|
+
return this.peers.map(p => p.name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/cross-brain/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,3 +16,5 @@ export type { McpHttpServerOptions } from './mcp/http-server.js';
|
|
|
16
16
|
export { c, baseIcons, header, keyValue, statusBadge, progressBar, divider, table, stripAnsi } from './cli/colors.js';
|
|
17
17
|
export { BaseApiServer } from './api/server.js';
|
|
18
18
|
export type { ApiServerOptions, RouteDefinition } from './api/server.js';
|
|
19
|
+
export { CrossBrainClient } from './cross-brain/client.js';
|
|
20
|
+
export type { BrainPeer } from './cross-brain/client.js';
|
package/dist/index.js
CHANGED
|
@@ -16,4 +16,6 @@ export { McpHttpServer } from './mcp/http-server.js';
|
|
|
16
16
|
export { c, baseIcons, header, keyValue, statusBadge, progressBar, divider, table, stripAnsi } from './cli/colors.js';
|
|
17
17
|
// ── API ────────────────────────────────────────────────────
|
|
18
18
|
export { BaseApiServer } from './api/server.js';
|
|
19
|
+
// ── Cross-Brain ────────────────────────────────────────────
|
|
20
|
+
export { CrossBrainClient } from './cross-brain/client.js';
|
|
19
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timmeck/brain-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Shared core infrastructure for the Brain ecosystem — IPC, MCP, CLI, DB connection, and utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"./mcp/server": "./dist/mcp/server.js",
|
|
20
20
|
"./mcp/http-server": "./dist/mcp/http-server.js",
|
|
21
21
|
"./cli/colors": "./dist/cli/colors.js",
|
|
22
|
-
"./api/server": "./dist/api/server.js"
|
|
22
|
+
"./api/server": "./dist/api/server.js",
|
|
23
|
+
"./cross-brain": "./dist/cross-brain/client.js"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
|
25
26
|
"build": "tsc",
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { IpcClient } from '../ipc/client.js';
|
|
2
|
+
import { getPipeName } from '../utils/paths.js';
|
|
3
|
+
|
|
4
|
+
export interface BrainPeer {
|
|
5
|
+
name: string;
|
|
6
|
+
pipeName: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const DEFAULT_PEERS: BrainPeer[] = [
|
|
10
|
+
{ name: 'brain', pipeName: getPipeName('brain') },
|
|
11
|
+
{ name: 'trading-brain', pipeName: getPipeName('trading-brain') },
|
|
12
|
+
{ name: 'marketing-brain', pipeName: getPipeName('marketing-brain') },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export class CrossBrainClient {
|
|
16
|
+
private peers: BrainPeer[];
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private selfName: string,
|
|
20
|
+
peers?: BrainPeer[],
|
|
21
|
+
) {
|
|
22
|
+
this.peers = (peers ?? DEFAULT_PEERS).filter(p => p.name !== selfName);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Query a specific peer brain by name.
|
|
27
|
+
* Returns null if the peer is not available.
|
|
28
|
+
*/
|
|
29
|
+
async query(peerName: string, method: string, params?: unknown): Promise<unknown | null> {
|
|
30
|
+
const peer = this.peers.find(p => p.name === peerName);
|
|
31
|
+
if (!peer) return null;
|
|
32
|
+
|
|
33
|
+
const client = new IpcClient(peer.pipeName, 3000);
|
|
34
|
+
try {
|
|
35
|
+
await client.connect();
|
|
36
|
+
const result = await client.request(method, params);
|
|
37
|
+
return result;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
} finally {
|
|
41
|
+
client.disconnect();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Broadcast a query to all available peer brains.
|
|
47
|
+
* Returns results from all peers that responded.
|
|
48
|
+
*/
|
|
49
|
+
async broadcast(method: string, params?: unknown): Promise<{ name: string; result: unknown }[]> {
|
|
50
|
+
const results: { name: string; result: unknown }[] = [];
|
|
51
|
+
|
|
52
|
+
const promises = this.peers.map(async (peer) => {
|
|
53
|
+
const client = new IpcClient(peer.pipeName, 3000);
|
|
54
|
+
try {
|
|
55
|
+
await client.connect();
|
|
56
|
+
const result = await client.request(method, params);
|
|
57
|
+
results.push({ name: peer.name, result });
|
|
58
|
+
} catch {
|
|
59
|
+
// Peer not available — skip
|
|
60
|
+
} finally {
|
|
61
|
+
client.disconnect();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await Promise.all(promises);
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check which peer brains are currently running.
|
|
71
|
+
*/
|
|
72
|
+
async getAvailablePeers(): Promise<string[]> {
|
|
73
|
+
const available: string[] = [];
|
|
74
|
+
|
|
75
|
+
const checks = this.peers.map(async (peer) => {
|
|
76
|
+
const client = new IpcClient(peer.pipeName, 1000);
|
|
77
|
+
try {
|
|
78
|
+
await client.connect();
|
|
79
|
+
available.push(peer.name);
|
|
80
|
+
} catch {
|
|
81
|
+
// Not available
|
|
82
|
+
} finally {
|
|
83
|
+
client.disconnect();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await Promise.all(checks);
|
|
88
|
+
return available;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getPeerNames(): string[] {
|
|
92
|
+
return this.peers.map(p => p.name);
|
|
93
|
+
}
|
|
94
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -29,3 +29,7 @@ export { c, baseIcons, header, keyValue, statusBadge, progressBar, divider, tabl
|
|
|
29
29
|
// ── API ────────────────────────────────────────────────────
|
|
30
30
|
export { BaseApiServer } from './api/server.js';
|
|
31
31
|
export type { ApiServerOptions, RouteDefinition } from './api/server.js';
|
|
32
|
+
|
|
33
|
+
// ── Cross-Brain ────────────────────────────────────────────
|
|
34
|
+
export { CrossBrainClient } from './cross-brain/client.js';
|
|
35
|
+
export type { BrainPeer } from './cross-brain/client.js';
|