synapse-causal-memory 0.2.2 → 0.2.3
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/causal-memory.json +1259 -344
- package/dist/causal-config.d.ts +4 -0
- package/dist/causal-peer.d.ts +53 -0
- package/dist/causal-server.d.ts +3 -2
- package/dist/causal-store.d.ts +26 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +532 -12
- package/dist/index.js +166 -6
- package/package.json +8 -2
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
1
2
|
// @bun
|
|
2
3
|
var __esm = (fn, res, err) => () => {
|
|
3
4
|
if (fn)
|
|
@@ -23,6 +24,8 @@ var init_causal_config = __esm(() => {
|
|
|
23
24
|
LOCAL_DB_PATH: resolve(import.meta.dir, "causal-memory.json"),
|
|
24
25
|
DEFAULT_HOST: process.env.SYNAPSE_MEMORY_HOST || "127.0.0.1",
|
|
25
26
|
DEFAULT_PORT: parseInt(process.env.SYNAPSE_MEMORY_PORT || "4242", 10),
|
|
27
|
+
REMOTE_URL: process.env.SYNAPSE_MEMORY_REMOTE_URL || "",
|
|
28
|
+
REMOTE_TIMEOUT_MS: parseInt(process.env.SYNAPSE_MEMORY_REMOTE_TIMEOUT || "3000", 10),
|
|
26
29
|
MCP_PROTOCOL_VERSION: process.env.SYNAPSE_MCP_VERSION || "2024-11-05",
|
|
27
30
|
MCP_SERVER_NAME: "synapse-causal-memory",
|
|
28
31
|
MAX_CONTEXT_PREVIEW_LINES: parseInt(process.env.SYNAPSE_MEMORY_MAX_PREVIEW || "5", 10)
|
|
@@ -181,6 +184,42 @@ class CausalMemoryStore {
|
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
}
|
|
187
|
+
static getRuleDomainPriority(rule) {
|
|
188
|
+
const titleLower = rule.title.toLowerCase();
|
|
189
|
+
const dom = rule.domain;
|
|
190
|
+
if (titleLower.includes("biophys") && dom === "User_Preferences_And_Rules") {
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
if (dom === "Knowledge_Base") {
|
|
194
|
+
return 1;
|
|
195
|
+
}
|
|
196
|
+
return 2;
|
|
197
|
+
}
|
|
198
|
+
static deduplicateRulesByTitle(rules) {
|
|
199
|
+
const byTitle = new Map;
|
|
200
|
+
for (const rule of rules) {
|
|
201
|
+
if (!rule.title)
|
|
202
|
+
continue;
|
|
203
|
+
const key = rule.title.toLowerCase();
|
|
204
|
+
const existing = byTitle.get(key);
|
|
205
|
+
if (!existing) {
|
|
206
|
+
byTitle.set(key, rule);
|
|
207
|
+
} else {
|
|
208
|
+
const existingPrio = CausalMemoryStore.getRuleDomainPriority(existing);
|
|
209
|
+
const incomingPrio = CausalMemoryStore.getRuleDomainPriority(rule);
|
|
210
|
+
if (incomingPrio > existingPrio) {
|
|
211
|
+
byTitle.set(key, rule);
|
|
212
|
+
} else if (incomingPrio === existingPrio) {
|
|
213
|
+
const existingDate = existing.lastVerified || "";
|
|
214
|
+
const incomingDate = rule.lastVerified || "";
|
|
215
|
+
if (incomingDate >= existingDate) {
|
|
216
|
+
byTitle.set(key, rule);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return Array.from(byTitle.values());
|
|
222
|
+
}
|
|
184
223
|
loadDatabase() {
|
|
185
224
|
try {
|
|
186
225
|
if (existsSync(this.dbPath)) {
|
|
@@ -188,7 +227,8 @@ class CausalMemoryStore {
|
|
|
188
227
|
this.lastMtimeMs = stat.mtimeMs;
|
|
189
228
|
const raw = readFileSync(this.dbPath, "utf-8");
|
|
190
229
|
const parsed = JSON.parse(raw);
|
|
191
|
-
const
|
|
230
|
+
const rawRules = Array.isArray(parsed.rules) ? parsed.rules : [];
|
|
231
|
+
const rules = CausalMemoryStore.deduplicateRulesByTitle(rawRules);
|
|
192
232
|
const relations = Array.isArray(parsed.relations) ? parsed.relations : [];
|
|
193
233
|
return {
|
|
194
234
|
version: parsed.version || CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
@@ -637,6 +677,34 @@ class CausalMemoryStore {
|
|
|
637
677
|
}
|
|
638
678
|
return created;
|
|
639
679
|
}
|
|
680
|
+
deleteRule(ruleId, persistToDisk = true) {
|
|
681
|
+
const targetId = ruleId.toLowerCase();
|
|
682
|
+
const removedTitles = new Set;
|
|
683
|
+
const filteredRules = this.database.rules.filter((r) => {
|
|
684
|
+
if (r.id.toLowerCase() === targetId) {
|
|
685
|
+
removedTitles.add(r.title.toLowerCase());
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
return true;
|
|
689
|
+
});
|
|
690
|
+
if (removedTitles.size === 0) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
const filteredRelations = this.database.relations.filter((rel) => !removedTitles.has(rel.from.toLowerCase()) && !removedTitles.has(rel.to.toLowerCase()) && rel.from.toLowerCase() !== targetId && rel.to.toLowerCase() !== targetId);
|
|
694
|
+
this.database = {
|
|
695
|
+
...this.database,
|
|
696
|
+
updatedAt: new Date().toISOString(),
|
|
697
|
+
totalRules: filteredRules.length,
|
|
698
|
+
totalRelations: filteredRelations.length,
|
|
699
|
+
rules: filteredRules,
|
|
700
|
+
relations: filteredRelations
|
|
701
|
+
};
|
|
702
|
+
this.rebuildIndices();
|
|
703
|
+
if (persistToDisk) {
|
|
704
|
+
this.saveDatabase();
|
|
705
|
+
}
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
640
708
|
deleteEntities(entityNames, persistToDisk = true) {
|
|
641
709
|
const nameSet = new Set(entityNames.map((n) => n.toLowerCase()));
|
|
642
710
|
const filteredRules = this.database.rules.filter((r) => !nameSet.has(r.title.toLowerCase()) && !nameSet.has(r.id.toLowerCase()));
|
|
@@ -701,7 +769,7 @@ class CausalMemoryStore {
|
|
|
701
769
|
const invariants = [];
|
|
702
770
|
const actions = [];
|
|
703
771
|
const context = [];
|
|
704
|
-
const isTrueUserPreference = typeLower === "preference" || typeLower === "user_constraint" || typeLower === "forbidden_rules" || nameLower === "userpreferences" || nameLower === "user_web_navigation_preference" || nameLower === "python_strictly_forbidden" || nameLower === "forbidden_topics_until_mentioned";
|
|
772
|
+
const isTrueUserPreference = !nameLower.includes("biophys") && (typeLower === "preference" || typeLower === "user_constraint" || typeLower === "forbidden_rules" || nameLower === "userpreferences" || nameLower === "user_web_navigation_preference" || nameLower === "python_strictly_forbidden" || nameLower === "forbidden_topics_until_mentioned");
|
|
705
773
|
if (isTrueUserPreference) {
|
|
706
774
|
domain = "User_Preferences_And_Rules";
|
|
707
775
|
triggers.alwaysActive = true;
|
|
@@ -763,6 +831,7 @@ class CausalMemoryStore {
|
|
|
763
831
|
audit() {
|
|
764
832
|
const issues = [];
|
|
765
833
|
const seenIds = new Set;
|
|
834
|
+
const seenTitles = new Set;
|
|
766
835
|
let alwaysActiveCount = 0;
|
|
767
836
|
for (const rule of this.database.rules) {
|
|
768
837
|
if (rule.triggers.alwaysActive) {
|
|
@@ -775,6 +844,14 @@ class CausalMemoryStore {
|
|
|
775
844
|
} else {
|
|
776
845
|
seenIds.add(rule.id);
|
|
777
846
|
}
|
|
847
|
+
const titleLower = (rule.title || "").toLowerCase();
|
|
848
|
+
if (titleLower) {
|
|
849
|
+
if (seenTitles.has(titleLower)) {
|
|
850
|
+
issues.push(`\uC911\uBCF5 \uC81C\uBAA9(Title) \uBC1C\uACAC: ${rule.title}`);
|
|
851
|
+
} else {
|
|
852
|
+
seenTitles.add(titleLower);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
778
855
|
if (!rule.domain) {
|
|
779
856
|
issues.push(`\uB3C4\uBA54\uC778 \uB204\uB77D \uADDC\uCE59: ${rule.id}`);
|
|
780
857
|
}
|
|
@@ -790,6 +867,76 @@ class CausalMemoryStore {
|
|
|
790
867
|
issues
|
|
791
868
|
};
|
|
792
869
|
}
|
|
870
|
+
getRawDatabase() {
|
|
871
|
+
this.reloadIfNeeded();
|
|
872
|
+
return this.database;
|
|
873
|
+
}
|
|
874
|
+
mergeDatabase(incomingRules, incomingRelations = []) {
|
|
875
|
+
this.reloadIfNeeded();
|
|
876
|
+
let addedRules = 0;
|
|
877
|
+
let updatedRules = 0;
|
|
878
|
+
let addedRelations = 0;
|
|
879
|
+
const byTitle = new Map;
|
|
880
|
+
for (const r of this.database.rules) {
|
|
881
|
+
byTitle.set(r.title.toLowerCase(), r);
|
|
882
|
+
}
|
|
883
|
+
for (const incoming of incomingRules) {
|
|
884
|
+
if (!incoming.id || !incoming.title)
|
|
885
|
+
continue;
|
|
886
|
+
const titleKey = incoming.title.toLowerCase();
|
|
887
|
+
const existing = byTitle.get(titleKey);
|
|
888
|
+
if (!existing) {
|
|
889
|
+
byTitle.set(titleKey, incoming);
|
|
890
|
+
addedRules++;
|
|
891
|
+
} else {
|
|
892
|
+
const existingPrio = CausalMemoryStore.getRuleDomainPriority(existing);
|
|
893
|
+
const incomingPrio = CausalMemoryStore.getRuleDomainPriority(incoming);
|
|
894
|
+
const existingDate = existing.lastVerified || "";
|
|
895
|
+
const incomingDate = incoming.lastVerified || "";
|
|
896
|
+
if (incomingPrio > existingPrio) {
|
|
897
|
+
byTitle.set(titleKey, incoming);
|
|
898
|
+
updatedRules++;
|
|
899
|
+
} else if (incomingPrio === existingPrio && incoming.id !== existing.id && incomingDate > existingDate) {
|
|
900
|
+
byTitle.set(titleKey, incoming);
|
|
901
|
+
updatedRules++;
|
|
902
|
+
} else if (incomingPrio === existingPrio && incoming.id === existing.id && incomingDate > existingDate) {
|
|
903
|
+
byTitle.set(titleKey, incoming);
|
|
904
|
+
updatedRules++;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const relationKey = (rel) => `${rel.from.toLowerCase()}::${rel.relationType.toLowerCase()}::${rel.to.toLowerCase()}`;
|
|
909
|
+
const relMap = new Map;
|
|
910
|
+
for (const rel of this.database.relations) {
|
|
911
|
+
relMap.set(relationKey(rel), rel);
|
|
912
|
+
}
|
|
913
|
+
for (const incomingRel of incomingRelations) {
|
|
914
|
+
const key = relationKey(incomingRel);
|
|
915
|
+
if (!relMap.has(key)) {
|
|
916
|
+
relMap.set(key, incomingRel);
|
|
917
|
+
addedRelations++;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
const mergedRules = Array.from(byTitle.values());
|
|
921
|
+
const mergedRelations = Array.from(relMap.values());
|
|
922
|
+
this.database = {
|
|
923
|
+
...this.database,
|
|
924
|
+
updatedAt: new Date().toISOString(),
|
|
925
|
+
totalRules: mergedRules.length,
|
|
926
|
+
totalRelations: mergedRelations.length,
|
|
927
|
+
rules: mergedRules,
|
|
928
|
+
relations: mergedRelations
|
|
929
|
+
};
|
|
930
|
+
this.rebuildIndices();
|
|
931
|
+
this.saveDatabase();
|
|
932
|
+
return {
|
|
933
|
+
addedRules,
|
|
934
|
+
updatedRules,
|
|
935
|
+
addedRelations,
|
|
936
|
+
totalRules: mergedRules.length,
|
|
937
|
+
totalRelations: mergedRelations.length
|
|
938
|
+
};
|
|
939
|
+
}
|
|
793
940
|
}
|
|
794
941
|
var init_causal_store = __esm(() => {
|
|
795
942
|
init_causal_config();
|
|
@@ -1129,6 +1276,17 @@ function startCausalServer(options = {}) {
|
|
|
1129
1276
|
const auditResult = store.audit();
|
|
1130
1277
|
return new Response(JSON.stringify(auditResult), { headers });
|
|
1131
1278
|
}
|
|
1279
|
+
if (url.pathname === "/sync" && req.method === "GET") {
|
|
1280
|
+
const rawDb = store.getRawDatabase();
|
|
1281
|
+
return new Response(JSON.stringify(rawDb), { headers });
|
|
1282
|
+
}
|
|
1283
|
+
if (url.pathname === "/sync" && req.method === "POST") {
|
|
1284
|
+
const body = await req.json();
|
|
1285
|
+
const incomingRules = Array.isArray(body.rules) ? body.rules : [];
|
|
1286
|
+
const incomingRelations = Array.isArray(body.relations) ? body.relations : [];
|
|
1287
|
+
const mergeResult = store.mergeDatabase(incomingRules, incomingRelations);
|
|
1288
|
+
return new Response(JSON.stringify({ ok: true, ...mergeResult }), { headers });
|
|
1289
|
+
}
|
|
1132
1290
|
if (url.pathname === "/mcp" && req.method === "POST") {
|
|
1133
1291
|
const rpc = await req.json();
|
|
1134
1292
|
const responseObj = await handleMcpJsonRpcRequest(rpc, store, options.dbPath);
|
|
@@ -1145,9 +1303,12 @@ function startCausalServer(options = {}) {
|
|
|
1145
1303
|
});
|
|
1146
1304
|
return server;
|
|
1147
1305
|
}
|
|
1148
|
-
async function startCausalMcpStdioServer(dbPath) {
|
|
1306
|
+
async function startCausalMcpStdioServer(dbPath, remoteUrl) {
|
|
1149
1307
|
const store = new CausalMemoryStore(dbPath);
|
|
1150
|
-
const
|
|
1308
|
+
const targetRemote = remoteUrl || CAUSAL_MEMORY_CONFIG.REMOTE_URL;
|
|
1309
|
+
const isRemoteMode = Boolean(targetRemote && targetRemote.trim().length > 0);
|
|
1310
|
+
const activeDaemonUrl = isRemoteMode ? targetRemote.endsWith("/mcp") ? targetRemote : `${targetRemote.replace(/\/+$/, "")}/mcp` : `http://${CAUSAL_MEMORY_CONFIG.DEFAULT_HOST}:${CAUSAL_MEMORY_CONFIG.DEFAULT_PORT}/mcp`;
|
|
1311
|
+
const timeoutMs = isRemoteMode ? CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS : 150;
|
|
1151
1312
|
const rl = createInterface({
|
|
1152
1313
|
input: process.stdin,
|
|
1153
1314
|
terminal: false
|
|
@@ -1161,11 +1322,11 @@ async function startCausalMcpStdioServer(dbPath) {
|
|
|
1161
1322
|
let responseObj = null;
|
|
1162
1323
|
if (rpc.method === "tools/call") {
|
|
1163
1324
|
try {
|
|
1164
|
-
const daemonRes = await fetch(
|
|
1325
|
+
const daemonRes = await fetch(activeDaemonUrl, {
|
|
1165
1326
|
method: "POST",
|
|
1166
1327
|
headers: { "Content-Type": "application/json" },
|
|
1167
1328
|
body: trimmed,
|
|
1168
|
-
signal: AbortSignal.timeout(
|
|
1329
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1169
1330
|
});
|
|
1170
1331
|
if (daemonRes.ok) {
|
|
1171
1332
|
responseObj = await daemonRes.json();
|
|
@@ -1930,6 +2091,177 @@ function migrateLegacyMemoryGraph(sourceBackupPath = CAUSAL_MEMORY_CONFIG.LEGACY
|
|
|
1930
2091
|
};
|
|
1931
2092
|
}
|
|
1932
2093
|
|
|
2094
|
+
// causal-peer.ts
|
|
2095
|
+
init_causal_config();
|
|
2096
|
+
init_causal_store();
|
|
2097
|
+
async function getTailscalePeers() {
|
|
2098
|
+
try {
|
|
2099
|
+
const proc = Bun.spawn(["tailscale", "status", "--json"], {
|
|
2100
|
+
stdout: "pipe",
|
|
2101
|
+
stderr: "pipe"
|
|
2102
|
+
});
|
|
2103
|
+
const output = await new Response(proc.stdout).text();
|
|
2104
|
+
const data = JSON.parse(output);
|
|
2105
|
+
const selfIp = data.Self?.TailscaleIPs?.[0];
|
|
2106
|
+
const selfHost = data.Self?.HostName;
|
|
2107
|
+
const peers = [];
|
|
2108
|
+
if (data.Self) {
|
|
2109
|
+
peers.push({
|
|
2110
|
+
id: data.Self.ID || "self",
|
|
2111
|
+
hostName: data.Self.HostName || "localhost",
|
|
2112
|
+
ip: selfIp || "127.0.0.1",
|
|
2113
|
+
os: data.Self.OS || process.platform,
|
|
2114
|
+
online: data.Self.Online ?? true,
|
|
2115
|
+
active: true,
|
|
2116
|
+
isSelf: true
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
if (data.Peer) {
|
|
2120
|
+
for (const key of Object.keys(data.Peer)) {
|
|
2121
|
+
const p = data.Peer[key];
|
|
2122
|
+
const peerIp = p.TailscaleIPs?.[0] || "";
|
|
2123
|
+
peers.push({
|
|
2124
|
+
id: p.ID || key,
|
|
2125
|
+
hostName: p.HostName || "",
|
|
2126
|
+
ip: peerIp,
|
|
2127
|
+
os: p.OS || "unknown",
|
|
2128
|
+
online: Boolean(p.Online),
|
|
2129
|
+
active: Boolean(p.Active),
|
|
2130
|
+
isSelf: false
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
return { selfIp, selfHost, peers };
|
|
2135
|
+
} catch (error) {
|
|
2136
|
+
return {
|
|
2137
|
+
peers: []
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
async function pingPeer(host, port = CAUSAL_MEMORY_CONFIG.DEFAULT_PORT) {
|
|
2142
|
+
const url = `http://${host}:${port}/health`;
|
|
2143
|
+
const startTime = performance.now();
|
|
2144
|
+
try {
|
|
2145
|
+
const res = await fetch(url, {
|
|
2146
|
+
method: "GET",
|
|
2147
|
+
signal: AbortSignal.timeout(CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS)
|
|
2148
|
+
});
|
|
2149
|
+
const rttMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
2150
|
+
if (!res.ok) {
|
|
2151
|
+
return {
|
|
2152
|
+
host,
|
|
2153
|
+
port,
|
|
2154
|
+
url,
|
|
2155
|
+
reachable: false,
|
|
2156
|
+
rttMs,
|
|
2157
|
+
error: `HTTP ${res.status} ${res.statusText}`
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
const data = await res.json();
|
|
2161
|
+
return {
|
|
2162
|
+
host,
|
|
2163
|
+
port,
|
|
2164
|
+
url,
|
|
2165
|
+
reachable: true,
|
|
2166
|
+
rttMs,
|
|
2167
|
+
version: data.status?.version,
|
|
2168
|
+
totalRules: data.status?.totalRules,
|
|
2169
|
+
totalRelations: data.status?.totalRelations
|
|
2170
|
+
};
|
|
2171
|
+
} catch (error) {
|
|
2172
|
+
const rttMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
2173
|
+
return {
|
|
2174
|
+
host,
|
|
2175
|
+
port,
|
|
2176
|
+
url,
|
|
2177
|
+
reachable: false,
|
|
2178
|
+
rttMs,
|
|
2179
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
async function queryRemotePeer(remoteUrl, options) {
|
|
2184
|
+
const cleanUrl = remoteUrl.replace(/\/+$/, "");
|
|
2185
|
+
const endpoint = cleanUrl.endsWith("/query") ? cleanUrl : `${cleanUrl}/query`;
|
|
2186
|
+
const res = await fetch(endpoint, {
|
|
2187
|
+
method: "POST",
|
|
2188
|
+
headers: { "Content-Type": "application/json" },
|
|
2189
|
+
body: JSON.stringify(options),
|
|
2190
|
+
signal: AbortSignal.timeout(CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS)
|
|
2191
|
+
});
|
|
2192
|
+
if (!res.ok) {
|
|
2193
|
+
throw new Error(`\uC6D0\uACA9 \uC778\uACFC\uC728 \uCFFC\uB9AC \uC2E4\uD328: HTTP ${res.status} ${res.statusText}`);
|
|
2194
|
+
}
|
|
2195
|
+
return await res.json();
|
|
2196
|
+
}
|
|
2197
|
+
async function syncWithPeer(remoteHost, port = CAUSAL_MEMORY_CONFIG.DEFAULT_PORT, direction = "pull", dbPath) {
|
|
2198
|
+
const store = new CausalMemoryStore(dbPath);
|
|
2199
|
+
const cleanHost = remoteHost.replace(/^https?:\/\//, "").split(":")[0];
|
|
2200
|
+
const targetPort = remoteHost.includes(":") ? parseInt(remoteHost.split(":")[1].replace(/\/.*/, ""), 10) || port : port;
|
|
2201
|
+
const baseUrl = `http://${cleanHost}:${targetPort}`;
|
|
2202
|
+
const startTime = performance.now();
|
|
2203
|
+
try {
|
|
2204
|
+
if (direction === "pull") {
|
|
2205
|
+
const res = await fetch(`${baseUrl}/sync`, {
|
|
2206
|
+
method: "GET",
|
|
2207
|
+
signal: AbortSignal.timeout(CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS)
|
|
2208
|
+
});
|
|
2209
|
+
if (!res.ok) {
|
|
2210
|
+
throw new Error(`\uC6D0\uACA9 \uB370\uC774\uD130 \uAC00\uC838\uC624\uAE30 \uC2E4\uD328: HTTP ${res.status}`);
|
|
2211
|
+
}
|
|
2212
|
+
const remoteDb = await res.json();
|
|
2213
|
+
const rules = Array.isArray(remoteDb.rules) ? remoteDb.rules : [];
|
|
2214
|
+
const relations = Array.isArray(remoteDb.relations) ? remoteDb.relations : [];
|
|
2215
|
+
const mergeResult = store.mergeDatabase(rules, relations);
|
|
2216
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
2217
|
+
return {
|
|
2218
|
+
targetHost: cleanHost,
|
|
2219
|
+
direction: "pull",
|
|
2220
|
+
success: true,
|
|
2221
|
+
durationMs,
|
|
2222
|
+
addedRules: mergeResult.addedRules,
|
|
2223
|
+
updatedRules: mergeResult.updatedRules,
|
|
2224
|
+
addedRelations: mergeResult.addedRelations,
|
|
2225
|
+
totalRules: mergeResult.totalRules,
|
|
2226
|
+
totalRelations: mergeResult.totalRelations
|
|
2227
|
+
};
|
|
2228
|
+
} else {
|
|
2229
|
+
const rawDb = store.getRawDatabase();
|
|
2230
|
+
const res = await fetch(`${baseUrl}/sync`, {
|
|
2231
|
+
method: "POST",
|
|
2232
|
+
headers: { "Content-Type": "application/json" },
|
|
2233
|
+
body: JSON.stringify(rawDb),
|
|
2234
|
+
signal: AbortSignal.timeout(CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS)
|
|
2235
|
+
});
|
|
2236
|
+
if (!res.ok) {
|
|
2237
|
+
throw new Error(`\uC6D0\uACA9 \uB370\uC774\uD130 \uD478\uC2DC \uC2E4\uD328: HTTP ${res.status}`);
|
|
2238
|
+
}
|
|
2239
|
+
const pushResult = await res.json();
|
|
2240
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
2241
|
+
return {
|
|
2242
|
+
targetHost: cleanHost,
|
|
2243
|
+
direction: "push",
|
|
2244
|
+
success: true,
|
|
2245
|
+
durationMs,
|
|
2246
|
+
addedRules: pushResult.addedRules,
|
|
2247
|
+
updatedRules: pushResult.updatedRules,
|
|
2248
|
+
addedRelations: pushResult.addedRelations,
|
|
2249
|
+
totalRules: pushResult.totalRules,
|
|
2250
|
+
totalRelations: pushResult.totalRelations
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
} catch (error) {
|
|
2254
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
2255
|
+
return {
|
|
2256
|
+
targetHost: cleanHost,
|
|
2257
|
+
direction,
|
|
2258
|
+
success: false,
|
|
2259
|
+
durationMs,
|
|
2260
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
1933
2265
|
// cli.ts
|
|
1934
2266
|
var RESET2 = "\x1B[0m";
|
|
1935
2267
|
var BOLD2 = "\x1B[1m";
|
|
@@ -1950,6 +2282,7 @@ ${BOLD2}\uC11C\uBE0C \uBA85\uB839\uC5B4:${RESET2}
|
|
|
1950
2282
|
list \uB4F1\uB85D\uB41C \uB3C4\uBA54\uC778, \uC778\uACFC \uADDC\uCE59 \uBC0F \uC778\uACFC \uAC04\uC120(Relations) \uD1B5\uACC4 \uD655\uC778
|
|
1951
2283
|
audit \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uBB34\uACB0\uC131 \uBC0F \uD1A0\uD070 \uC808\uAC10 \uC0C1\uD0DC \uAC10\uC0AC
|
|
1952
2284
|
assert \uC2E0\uADDC \uC778\uACFC\uC728 \uADDC\uCE59 \uB4F1\uB85D \uB610\uB294 \uAC31\uC2E0 (--json '<json>')
|
|
2285
|
+
peer Tailscale \uC0AC\uC124 \uBA54\uC26C \uD53C\uC5B4 \uD0D0\uC0C9, \uD551(RTT) \uBC0F \uBA54\uBAA8\uB9AC \uC6D0\uACA9 \uB3D9\uAE30\uD654
|
|
1953
2286
|
migrate \uAD6C\uD615 \uC9C0\uC2DD \uADF8\uB798\uD504 \uBC31\uC5C5\uBCF8\uC744 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB85C \uBB34\uC190\uC2E4 \uBCC0\uD658
|
|
1954
2287
|
benchmark 4\uB300 \uCC28\uC6D0 \uACFC\uBD80\uD558 \uC2A4\uD2B8\uB808\uC2A4 \uBCA4\uCE58\uB9C8\uD06C \uC2E4\uD589 (--scale 10000 --concurrency 50)
|
|
1955
2288
|
serve \uB3C5\uB9BD \uB85C\uCEEC \uBC31\uADF8\uB77C\uC6B4\uB4DC \uC11C\uBC84/\uB370\uBAAC \uAD6C\uB3D9 (0ms \uC778\uBA54\uBAA8\uB9AC HTTP & MCP API)
|
|
@@ -1960,11 +2293,21 @@ ${BOLD2}query \uC635\uC158:${RESET2}
|
|
|
1960
2293
|
--keywords <words...> \uD2B8\uB9AC\uAC70 \uAC80\uC0C9 \uD0A4\uC6CC\uB4DC
|
|
1961
2294
|
--command <cmd> \uD2B8\uB9AC\uAC70 \uC5F0\uB3D9 \uBA85\uB839\uC5B4 (\uC608: release, build, sign)
|
|
1962
2295
|
--domain <domain> \uD2B9\uC815 \uB3C4\uBA54\uC778\uC73C\uB85C \uAC80\uC0C9 \uC81C\uD55C
|
|
2296
|
+
--remote <url> \uC6D0\uACA9 \uB370\uBAAC\uC73C\uB85C \uACF5\uBA85 \uCFFC\uB9AC \uC694\uCCAD (\uC608: http://100.82.92.122:4242)
|
|
1963
2297
|
--format <json|md|compact> \uCD9C\uB825 \uD3EC\uB9F7 \uC9C0\uC815
|
|
1964
2298
|
|
|
1965
2299
|
${BOLD2}serve \uC635\uC158:${RESET2}
|
|
1966
2300
|
--port <number> \uC11C\uBC84 \uD3EC\uD2B8 \uBC88\uD638 (\uAE30\uBCF8: ${CAUSAL_MEMORY_CONFIG.DEFAULT_PORT})
|
|
1967
2301
|
--host <string> \uBC14\uC778\uB529 \uD638\uC2A4\uD2B8 (\uAE30\uBCF8: ${CAUSAL_MEMORY_CONFIG.DEFAULT_HOST})
|
|
2302
|
+
--tailscale Tailscale \uC0AC\uC124 IP\uB85C \uC790\uB3D9 \uBC14\uC778\uB529 (\uBA54\uC26C \uACF5\uC720 \uBAA8\uB4DC)
|
|
2303
|
+
|
|
2304
|
+
${BOLD2}peer \uC0AC\uC6A9\uBC95:${RESET2}
|
|
2305
|
+
synapse memory peer list Tailscale \uBA54\uC26C \uD53C\uC5B4 \uBC0F \uB370\uBAAC \uAC00\uB3D9 \uC0C1\uD0DC \uC870\uD68C
|
|
2306
|
+
synapse memory peer ping <ip> \uB300\uC0C1 \uD53C\uC5B4\uC758 Causal Memory \uD5EC\uC2A4\uCCB4\uD06C \uBC0F RTT \uCE21\uC815
|
|
2307
|
+
synapse memory peer sync <ip> [--push] \uB300\uC0C1 \uD53C\uC5B4\uC640 \uC778\uACFC\uC728 \uC9C0\uC2DD \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654
|
|
2308
|
+
|
|
2309
|
+
${BOLD2}mcp-stdio \uC635\uC158:${RESET2}
|
|
2310
|
+
--remote <url> \uC6D0\uACA9 \uB370\uBAAC \uD504\uB85D\uC2DC \uBAA8\uB4DC \uAD6C\uB3D9 (\uC608: http://100.82.92.122:4242)
|
|
1968
2311
|
|
|
1969
2312
|
${BOLD2}\uACF5\uD1B5 \uD50C\uB798\uADF8:${RESET2}
|
|
1970
2313
|
--agent, --compact AI \uC5D0\uC774\uC804\uD2B8 \uC804\uC6A9 \uCD08\uACBD\uB7C9 JSON \uBAA8\uB4DC
|
|
@@ -1979,7 +2322,14 @@ async function handleMemoryCommand(args, options) {
|
|
|
1979
2322
|
return 0;
|
|
1980
2323
|
}
|
|
1981
2324
|
if (subCommand === "mcp-stdio") {
|
|
1982
|
-
|
|
2325
|
+
let remoteUrl;
|
|
2326
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
2327
|
+
if (subArgs[i] === "--remote" && subArgs[i + 1]) {
|
|
2328
|
+
remoteUrl = subArgs[i + 1];
|
|
2329
|
+
break;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
await startCausalMcpStdioServer(undefined, remoteUrl);
|
|
1983
2333
|
return 0;
|
|
1984
2334
|
}
|
|
1985
2335
|
if (subCommand === "benchmark") {
|
|
@@ -2051,6 +2401,7 @@ ${BOLD2}${GREEN2}\u2728 [Causal Memory \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC6
|
|
|
2051
2401
|
const keywords = [];
|
|
2052
2402
|
let command;
|
|
2053
2403
|
let domain;
|
|
2404
|
+
let remoteUrl;
|
|
2054
2405
|
let format = options.compactMode ? "compact" : options.agentMode ? "json" : "text";
|
|
2055
2406
|
for (let i = 0;i < subArgs.length; i++) {
|
|
2056
2407
|
const arg = subArgs[i];
|
|
@@ -2072,11 +2423,13 @@ ${BOLD2}${GREEN2}\u2728 [Causal Memory \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC6
|
|
|
2072
2423
|
command = subArgs[++i];
|
|
2073
2424
|
} else if (arg === "--domain" || arg === "-d") {
|
|
2074
2425
|
domain = subArgs[++i];
|
|
2426
|
+
} else if (arg === "--remote") {
|
|
2427
|
+
remoteUrl = subArgs[++i];
|
|
2075
2428
|
} else if (arg === "--format") {
|
|
2076
2429
|
format = subArgs[++i];
|
|
2077
2430
|
}
|
|
2078
2431
|
}
|
|
2079
|
-
const result = await runCausalQuery({
|
|
2432
|
+
const result = remoteUrl ? await queryRemotePeer(remoteUrl, { files, keywords, command, domain }) : await runCausalQuery({
|
|
2080
2433
|
files,
|
|
2081
2434
|
keywords,
|
|
2082
2435
|
command,
|
|
@@ -2194,9 +2547,160 @@ ${BOLD2}${RED2}\uD83D\uDEA8 [Causal Memory Audit \uACB0\uD568 \uBC1C\uACAC] \uCD
|
|
|
2194
2547
|
return 1;
|
|
2195
2548
|
}
|
|
2196
2549
|
}
|
|
2550
|
+
if (subCommand === "peer") {
|
|
2551
|
+
const peerAction = subArgs.find((a) => a !== "peer" && !a.startsWith("-")) || "list";
|
|
2552
|
+
if (peerAction === "list") {
|
|
2553
|
+
const { selfIp, selfHost, peers } = await getTailscalePeers();
|
|
2554
|
+
if (peers.length === 0) {
|
|
2555
|
+
if (options.agentMode) {
|
|
2556
|
+
process.stdout.write(JSON.stringify({ ok: false, message: "No Tailscale peers detected" }) + `
|
|
2557
|
+
`);
|
|
2558
|
+
} else {
|
|
2559
|
+
process.stdout.write(`${YELLOW2}\u26A0\uFE0F Tailscale\uC774 \uC2E4\uD589 \uC911\uC774\uC9C0 \uC54A\uAC70\uB098 \uAC10\uC9C0\uB41C \uD53C\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.${RESET2}
|
|
2560
|
+
`);
|
|
2561
|
+
}
|
|
2562
|
+
return 1;
|
|
2563
|
+
}
|
|
2564
|
+
const healthPromises = peers.map(async (p) => {
|
|
2565
|
+
if (!p.ip) {
|
|
2566
|
+
return {
|
|
2567
|
+
...p,
|
|
2568
|
+
reachable: false,
|
|
2569
|
+
rttMs: -1,
|
|
2570
|
+
totalRules: undefined,
|
|
2571
|
+
totalRelations: undefined
|
|
2572
|
+
};
|
|
2573
|
+
}
|
|
2574
|
+
const health = await pingPeer(p.ip);
|
|
2575
|
+
return {
|
|
2576
|
+
...p,
|
|
2577
|
+
reachable: health.reachable,
|
|
2578
|
+
rttMs: health.rttMs,
|
|
2579
|
+
totalRules: health.totalRules,
|
|
2580
|
+
totalRelations: health.totalRelations
|
|
2581
|
+
};
|
|
2582
|
+
});
|
|
2583
|
+
const checkedPeers = await Promise.all(healthPromises);
|
|
2584
|
+
if (options.agentMode) {
|
|
2585
|
+
process.stdout.write(JSON.stringify({ ok: true, selfIp, selfHost, peers: checkedPeers }) + `
|
|
2586
|
+
`);
|
|
2587
|
+
return 0;
|
|
2588
|
+
}
|
|
2589
|
+
process.stdout.write(`
|
|
2590
|
+
${BOLD2}${CYAN2}\uD83C\uDF10 [Causal Memory] Tailscale \uC0AC\uC124 \uBA54\uC26C \uD53C\uC5B4 \uD1A0\uD3F4\uB85C\uC9C0${RESET2}
|
|
2591
|
+
`);
|
|
2592
|
+
process.stdout.write(` \u2022 \uB85C\uCEEC \uB178\uB4DC: ${BOLD2}${GREEN2}${selfHost || "localhost"}${RESET2} (${selfIp || "127.0.0.1"})
|
|
2593
|
+
|
|
2594
|
+
`);
|
|
2595
|
+
for (const p of checkedPeers) {
|
|
2596
|
+
const statusBadge = p.isSelf ? `${BOLD2}${CYAN2}[LOCAL HOST]${RESET2}` : p.reachable ? `${BOLD2}${GREEN2}[DAEMON ONLINE]${RESET2}` : p.online ? `${YELLOW2}[PEER UP (NO DAEMON)]${RESET2}` : `${GRAY2}[OFFLINE]${RESET2}`;
|
|
2597
|
+
const rttText = p.rttMs >= 0 ? `${p.rttMs}ms` : "-";
|
|
2598
|
+
const memoryInfo = p.reachable ? `(\uADDC\uCE59 ${p.totalRules}\uAC1C, \uAC04\uC120 ${p.totalRelations}\uAC1C)` : "";
|
|
2599
|
+
process.stdout.write(` \u2022 ${BOLD2}${p.hostName.padEnd(24)}${RESET2} ${GRAY2}${p.ip.padEnd(16)}${RESET2} ${statusBadge} ${GRAY2}${rttText}${RESET2} ${memoryInfo}
|
|
2600
|
+
`);
|
|
2601
|
+
}
|
|
2602
|
+
process.stdout.write(`
|
|
2603
|
+
`);
|
|
2604
|
+
return 0;
|
|
2605
|
+
}
|
|
2606
|
+
if (peerAction === "ping") {
|
|
2607
|
+
const targetHost = subArgs.filter((a) => a !== "peer" && a !== "ping" && !a.startsWith("-"))[0];
|
|
2608
|
+
if (!targetHost) {
|
|
2609
|
+
process.stderr.write(`${RED2}\u274C \uB300\uC0C1 IP \uB610\uB294 \uD638\uC2A4\uD2B8\uBA85\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (\uC608: synapse memory peer ping 100.73.80.80)${RESET2}
|
|
2610
|
+
`);
|
|
2611
|
+
return 1;
|
|
2612
|
+
}
|
|
2613
|
+
const health = await pingPeer(targetHost);
|
|
2614
|
+
if (options.agentMode) {
|
|
2615
|
+
process.stdout.write(JSON.stringify(health) + `
|
|
2616
|
+
`);
|
|
2617
|
+
return health.reachable ? 0 : 1;
|
|
2618
|
+
}
|
|
2619
|
+
if (health.reachable) {
|
|
2620
|
+
process.stdout.write(`
|
|
2621
|
+
${BOLD2}${GREEN2}\u2714 [Causal Memory Peer \uC751\uB2F5 \uC131\uACF5]${RESET2}
|
|
2622
|
+
`);
|
|
2623
|
+
process.stdout.write(` \u2022 \uD53C\uC5B4 \uC8FC\uC18C : ${CYAN2}${health.url}${RESET2}
|
|
2624
|
+
`);
|
|
2625
|
+
process.stdout.write(` \u2022 \uC751\uB2F5 \uC9C0\uC5F0 : ${BOLD2}${health.rttMs}ms${RESET2}
|
|
2626
|
+
`);
|
|
2627
|
+
process.stdout.write(` \u2022 \uC5D4\uC9C4 \uBC84\uC804 : v${health.version}
|
|
2628
|
+
`);
|
|
2629
|
+
process.stdout.write(` \u2022 \uBCF4\uC720 \uADDC\uCE59 : ${BOLD2}${health.totalRules}\uAC1C${RESET2} (\uAC04\uC120: ${health.totalRelations}\uAC1C)
|
|
2630
|
+
|
|
2631
|
+
`);
|
|
2632
|
+
return 0;
|
|
2633
|
+
} else {
|
|
2634
|
+
process.stderr.write(`
|
|
2635
|
+
${BOLD2}${RED2}\u2716 [Causal Memory Peer \uC751\uB2F5 \uC5C6\uC74C]${RESET2}
|
|
2636
|
+
`);
|
|
2637
|
+
process.stderr.write(` \u2022 \uD53C\uC5B4 \uC8FC\uC18C : ${health.url}
|
|
2638
|
+
`);
|
|
2639
|
+
process.stderr.write(` \u2022 \uB808\uC774\uD134\uC2DC : ${health.rttMs}ms
|
|
2640
|
+
`);
|
|
2641
|
+
process.stderr.write(` \u2022 \uC5D0\uB7EC \uB0B4\uC6A9 : ${health.error || "\uC5F0\uACB0 \uAC70\uBD80 \uB610\uB294 \uD0C0\uC784\uC544\uC6C3"}
|
|
2642
|
+
`);
|
|
2643
|
+
process.stderr.write(` \u2022 \uC870\uCE58 \uD301 : \uC0C1\uB300\uBC29 PC\uC5D0\uC11C 'synapse memory serve --tailscale' \uB370\uBAAC\uC774 \uC2E4\uD589 \uC911\uC778\uC9C0 \uD655\uC778\uD558\uC138\uC694.
|
|
2644
|
+
|
|
2645
|
+
`);
|
|
2646
|
+
return 1;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
if (peerAction === "sync") {
|
|
2650
|
+
const targetHost = subArgs.filter((a) => a !== "peer" && a !== "sync" && !a.startsWith("-"))[0];
|
|
2651
|
+
if (!targetHost) {
|
|
2652
|
+
process.stderr.write(`${RED2}\u274C \uB3D9\uAE30\uD654 \uB300\uC0C1 IP\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. (\uC608: synapse memory peer sync 100.73.80.80)${RESET2}
|
|
2653
|
+
`);
|
|
2654
|
+
return 1;
|
|
2655
|
+
}
|
|
2656
|
+
const isPush = subArgs.includes("--push");
|
|
2657
|
+
const direction = isPush ? "push" : "pull";
|
|
2658
|
+
const syncResult = await syncWithPeer(targetHost, CAUSAL_MEMORY_CONFIG.DEFAULT_PORT, direction);
|
|
2659
|
+
if (options.agentMode) {
|
|
2660
|
+
process.stdout.write(JSON.stringify(syncResult) + `
|
|
2661
|
+
`);
|
|
2662
|
+
return syncResult.success ? 0 : 1;
|
|
2663
|
+
}
|
|
2664
|
+
if (syncResult.success) {
|
|
2665
|
+
process.stdout.write(`
|
|
2666
|
+
${BOLD2}${GREEN2}\u2728 [Causal Memory \uB3D9\uAE30\uD654 \uC131\uACF5 (${direction.toUpperCase()})]${RESET2}
|
|
2667
|
+
`);
|
|
2668
|
+
process.stdout.write(` \u2022 \uB300\uC0C1 \uD53C\uC5B4 : ${CYAN2}${syncResult.targetHost}${RESET2}
|
|
2669
|
+
`);
|
|
2670
|
+
process.stdout.write(` \u2022 \uCD94\uAC00\uB41C \uADDC\uCE59: ${BOLD2}${syncResult.addedRules}\uAC1C${RESET2} (\uAC31\uC2E0: ${syncResult.updatedRules}\uAC1C)
|
|
2671
|
+
`);
|
|
2672
|
+
process.stdout.write(` \u2022 \uCD94\uAC00\uB41C \uAC04\uC120: ${BOLD2}${syncResult.addedRelations}\uAC1C${RESET2}
|
|
2673
|
+
`);
|
|
2674
|
+
process.stdout.write(` \u2022 \uCD1D \uC9C0\uC2DD \uC218 : \uADDC\uCE59 ${syncResult.totalRules}\uAC1C, \uAC04\uC120 ${syncResult.totalRelations}\uAC1C
|
|
2675
|
+
`);
|
|
2676
|
+
process.stdout.write(` \u2022 \uC18C\uC694 \uC2DC\uAC04 : ${syncResult.durationMs}ms
|
|
2677
|
+
|
|
2678
|
+
`);
|
|
2679
|
+
return 0;
|
|
2680
|
+
} else {
|
|
2681
|
+
process.stderr.write(`
|
|
2682
|
+
${BOLD2}${RED2}\uD83D\uDEA8 [Causal Memory \uB3D9\uAE30\uD654 \uC2E4\uD328]${RESET2}
|
|
2683
|
+
`);
|
|
2684
|
+
process.stderr.write(` \u2022 \uB300\uC0C1 \uD53C\uC5B4 : ${syncResult.targetHost}
|
|
2685
|
+
`);
|
|
2686
|
+
process.stderr.write(` \u2022 \uC5D0\uB7EC \uC0AC\uC720 : ${syncResult.error}
|
|
2687
|
+
|
|
2688
|
+
`);
|
|
2689
|
+
return 1;
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
process.stderr.write(`${RED2}\u274C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 peer \uC11C\uBE0C \uBA85\uB839\uC5B4: '${peerAction}' (list, ping, sync)${RESET2}
|
|
2693
|
+
`);
|
|
2694
|
+
return 1;
|
|
2695
|
+
}
|
|
2197
2696
|
if (subCommand === "serve") {
|
|
2198
2697
|
let port = CAUSAL_MEMORY_CONFIG.DEFAULT_PORT;
|
|
2199
2698
|
let host = CAUSAL_MEMORY_CONFIG.DEFAULT_HOST;
|
|
2699
|
+
const isTailscale = subArgs.includes("--tailscale");
|
|
2700
|
+
if (isTailscale) {
|
|
2701
|
+
const { selfIp } = await getTailscalePeers();
|
|
2702
|
+
host = selfIp || "0.0.0.0";
|
|
2703
|
+
}
|
|
2200
2704
|
for (let i = 0;i < subArgs.length; i++) {
|
|
2201
2705
|
if (subArgs[i] === "--port" && subArgs[i + 1]) {
|
|
2202
2706
|
port = parseInt(subArgs[i + 1], 10);
|
|
@@ -2205,14 +2709,15 @@ ${BOLD2}${RED2}\uD83D\uDEA8 [Causal Memory Audit \uACB0\uD568 \uBC1C\uACAC] \uCD
|
|
|
2205
2709
|
}
|
|
2206
2710
|
}
|
|
2207
2711
|
const server = startCausalServer({ port, host });
|
|
2208
|
-
if (options
|
|
2209
|
-
process.stdout.write(JSON.stringify({ ok: true, port: server.port, host, url: `http://${host}:${server.port}
|
|
2712
|
+
if (options?.agentMode) {
|
|
2713
|
+
process.stdout.write(JSON.stringify({ ok: true, port: server.port, host, url: `http://${host}:${server.port}`, tailscale: isTailscale }) + `
|
|
2210
2714
|
`);
|
|
2211
2715
|
} else {
|
|
2212
2716
|
process.stdout.write(`
|
|
2213
2717
|
${BOLD2}${GREEN2}\uD83D\uDE80 [Causal Memory Daemon \uAC00\uB3D9 \uC644\uB8CC]${RESET2}
|
|
2214
|
-
\u2022 \
|
|
2215
|
-
\u2022 MCP API: ${GRAY2}http://${host}:${server.port}/mcp${RESET2}
|
|
2718
|
+
\u2022 \uB85C\uCEEC \uBC14\uC778\uB529: ${CYAN2}http://${host}:${server.port}${RESET2}
|
|
2719
|
+
\u2022 MCP API URL: ${GRAY2}http://${host}:${server.port}/mcp${RESET2}
|
|
2720
|
+
\u2022 \uBAA8\uB4DC : ${isTailscale ? `${BOLD2}${GREEN2}Tailscale \uC0AC\uC124 \uBA54\uC26C \uBAA8\uB4DC (\uC678\uBD80 \uD53C\uC5B4 \uC811\uADFC \uD5C8\uC6A9)${RESET2}` : "\uB85C\uCEEC \uB8E8\uD504\uBC31 \uBAA8\uB4DC"}
|
|
2216
2721
|
\u2022 0ms \uC778\uBA54\uBAA8\uB9AC \uACF5\uBA85 \uB300\uAE30 \uC911... (\uC885\uB8CC: Ctrl+C)
|
|
2217
2722
|
|
|
2218
2723
|
`);
|
|
@@ -2225,6 +2730,21 @@ ${BOLD2}${GREEN2}\uD83D\uDE80 [Causal Memory Daemon \uAC00\uB3D9 \uC644\uB8CC]${
|
|
|
2225
2730
|
printMemoryHelp();
|
|
2226
2731
|
return 1;
|
|
2227
2732
|
}
|
|
2733
|
+
if (import.meta.main) {
|
|
2734
|
+
const rawArgs = process.argv.slice(2);
|
|
2735
|
+
const options = {
|
|
2736
|
+
agentMode: rawArgs.includes("--agent") || rawArgs.includes("--compact"),
|
|
2737
|
+
compact: rawArgs.includes("--compact"),
|
|
2738
|
+
force: rawArgs.includes("--force") || rawArgs.includes("-f")
|
|
2739
|
+
};
|
|
2740
|
+
handleMemoryCommand(rawArgs, options).then((code) => {
|
|
2741
|
+
process.exit(code);
|
|
2742
|
+
}).catch((err) => {
|
|
2743
|
+
process.stderr.write(`\u274C Error: ${err instanceof Error ? err.message : String(err)}
|
|
2744
|
+
`);
|
|
2745
|
+
process.exit(1);
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2228
2748
|
export {
|
|
2229
2749
|
handleMemoryCommand
|
|
2230
2750
|
};
|