conare 0.3.1 → 0.3.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/dist/index.js +189 -180
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -875,10 +875,10 @@ import { existsSync as existsSync9 } from "node:fs";
|
|
|
875
875
|
import { join as join10 } from "node:path";
|
|
876
876
|
|
|
877
877
|
// src/detect.ts
|
|
878
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
879
|
-
import { spawnSync } from "node:child_process";
|
|
878
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
880
879
|
import { join } from "node:path";
|
|
881
880
|
import { homedir, platform } from "node:os";
|
|
881
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
882
882
|
function countJsonlFiles(dir) {
|
|
883
883
|
let count = 0;
|
|
884
884
|
try {
|
|
@@ -892,50 +892,41 @@ function countJsonlFiles(dir) {
|
|
|
892
892
|
} catch {}
|
|
893
893
|
return count;
|
|
894
894
|
}
|
|
895
|
-
function countCursorSessions(dbPath) {
|
|
895
|
+
async function countCursorSessions(dbPath) {
|
|
896
896
|
try {
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
)
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
)
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
HAVING SUM(CASE WHEN type = 1 THEN 1 ELSE 0 END) > 0
|
|
926
|
-
AND SUM(CASE WHEN type = 2 THEN 1 ELSE 0 END) > 0
|
|
927
|
-
);
|
|
928
|
-
`.trim()
|
|
929
|
-
], { encoding: "utf-8" });
|
|
930
|
-
if (result.status !== 0)
|
|
931
|
-
return 0;
|
|
932
|
-
const count = Number.parseInt(result.stdout.trim(), 10);
|
|
933
|
-
return Number.isFinite(count) ? count : 0;
|
|
897
|
+
const require2 = createRequire2(import.meta.url);
|
|
898
|
+
const initSqlJs = require2("sql.js");
|
|
899
|
+
const SQL = await initSqlJs();
|
|
900
|
+
const buffer = readFileSync(dbPath);
|
|
901
|
+
const db = new SQL.Database(buffer);
|
|
902
|
+
try {
|
|
903
|
+
const results = db.exec("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'");
|
|
904
|
+
if (results.length === 0)
|
|
905
|
+
return 0;
|
|
906
|
+
let count = 0;
|
|
907
|
+
for (const [, value] of results[0].values) {
|
|
908
|
+
try {
|
|
909
|
+
const parsed = JSON.parse(value);
|
|
910
|
+
const headers = parsed.fullConversationHeadersOnly;
|
|
911
|
+
if (!Array.isArray(headers) || headers.length < 2)
|
|
912
|
+
continue;
|
|
913
|
+
const hasUser = headers.some((h) => h.type === 1);
|
|
914
|
+
const hasAssistant = headers.some((h) => h.type === 2);
|
|
915
|
+
if (hasUser && hasAssistant)
|
|
916
|
+
count++;
|
|
917
|
+
} catch {
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return count;
|
|
922
|
+
} finally {
|
|
923
|
+
db.close();
|
|
924
|
+
}
|
|
934
925
|
} catch {
|
|
935
926
|
return 0;
|
|
936
927
|
}
|
|
937
928
|
}
|
|
938
|
-
function detect() {
|
|
929
|
+
async function detect() {
|
|
939
930
|
const home = homedir();
|
|
940
931
|
const tools = [];
|
|
941
932
|
const claudeDir = join(home, ".claude", "projects");
|
|
@@ -956,8 +947,8 @@ function detect() {
|
|
|
956
947
|
let sessionCount = 0;
|
|
957
948
|
if (existsSync(codexHistory)) {
|
|
958
949
|
try {
|
|
959
|
-
const { readFileSync } = __require("node:fs");
|
|
960
|
-
const lines =
|
|
950
|
+
const { readFileSync: readFileSync2 } = __require("node:fs");
|
|
951
|
+
const lines = readFileSync2(codexHistory, "utf-8").split(`
|
|
961
952
|
`).filter(Boolean);
|
|
962
953
|
const sessions = new Set(lines.map((l) => {
|
|
963
954
|
try {
|
|
@@ -992,7 +983,7 @@ function detect() {
|
|
|
992
983
|
name: "Cursor",
|
|
993
984
|
available: existsSync(cursorDbPath),
|
|
994
985
|
path: cursorDbPath,
|
|
995
|
-
sessionCount: existsSync(cursorDbPath) ? countCursorSessions(cursorDbPath) : 0
|
|
986
|
+
sessionCount: existsSync(cursorDbPath) ? await countCursorSessions(cursorDbPath) : 0
|
|
996
987
|
});
|
|
997
988
|
const openclawDir = join(home, ".openclaw");
|
|
998
989
|
tools.push({
|
|
@@ -1005,12 +996,12 @@ function detect() {
|
|
|
1005
996
|
}
|
|
1006
997
|
|
|
1007
998
|
// src/ingest/claude.ts
|
|
1008
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
999
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
|
|
1009
1000
|
import { join as join3, basename } from "node:path";
|
|
1010
|
-
import { homedir as homedir3 } from "node:os";
|
|
1001
|
+
import { homedir as homedir3, platform as platform2 } from "node:os";
|
|
1011
1002
|
|
|
1012
1003
|
// src/ingest/shared.ts
|
|
1013
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
1004
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "node:fs";
|
|
1014
1005
|
import { createHash } from "node:crypto";
|
|
1015
1006
|
import { join as join2 } from "node:path";
|
|
1016
1007
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -1036,6 +1027,9 @@ function cleanText(raw) {
|
|
|
1036
1027
|
text = text.replace(/<good-behaviour>[\s\S]*?<\/good-behaviour>/g, "");
|
|
1037
1028
|
text = text.replace(/<frontend-design>[\s\S]*?<\/frontend-design>/g, "");
|
|
1038
1029
|
text = text.replace(/<architecture>[\s\S]*?<\/architecture>/g, "");
|
|
1030
|
+
text = text.replace(/<task-notification>[\s\S]*?<\/task-notification>/g, "");
|
|
1031
|
+
text = text.replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, "");
|
|
1032
|
+
text = text.replace(/<system_instruction>[\s\S]*?<\/system_instruction>/g, "");
|
|
1039
1033
|
return text.trim();
|
|
1040
1034
|
}
|
|
1041
1035
|
function createContentHash(content) {
|
|
@@ -1044,7 +1038,7 @@ function createContentHash(content) {
|
|
|
1044
1038
|
function getIngested() {
|
|
1045
1039
|
try {
|
|
1046
1040
|
if (existsSync2(MANIFEST_PATH)) {
|
|
1047
|
-
return JSON.parse(
|
|
1041
|
+
return JSON.parse(readFileSync2(MANIFEST_PATH, "utf-8"));
|
|
1048
1042
|
}
|
|
1049
1043
|
} catch {}
|
|
1050
1044
|
return {};
|
|
@@ -1083,8 +1077,17 @@ var MAX_CONTENT = 48000;
|
|
|
1083
1077
|
var MIN_TURN_LEN = 50;
|
|
1084
1078
|
function resolveProjectName(dirName) {
|
|
1085
1079
|
const segments = dirName.replace(/^-/, "").split("-");
|
|
1086
|
-
|
|
1087
|
-
let
|
|
1080
|
+
const isWindows = platform2() === "win32";
|
|
1081
|
+
let resolved;
|
|
1082
|
+
let startIdx;
|
|
1083
|
+
if (isWindows && segments.length > 0 && /^[A-Za-z]$/.test(segments[0])) {
|
|
1084
|
+
resolved = segments[0].toUpperCase() + ":\\";
|
|
1085
|
+
startIdx = 1;
|
|
1086
|
+
} else {
|
|
1087
|
+
resolved = "/";
|
|
1088
|
+
startIdx = 0;
|
|
1089
|
+
}
|
|
1090
|
+
let i = startIdx;
|
|
1088
1091
|
while (i < segments.length) {
|
|
1089
1092
|
let found = false;
|
|
1090
1093
|
for (let end = segments.length;end > i; end--) {
|
|
@@ -1103,10 +1106,11 @@ function resolveProjectName(dirName) {
|
|
|
1103
1106
|
}
|
|
1104
1107
|
}
|
|
1105
1108
|
const home = homedir3();
|
|
1106
|
-
|
|
1107
|
-
|
|
1109
|
+
const sep = isWindows ? "\\" : "/";
|
|
1110
|
+
if (resolved.startsWith(home + sep)) {
|
|
1111
|
+
return resolved.slice(home.length + 1).replace(/\\/g, "/");
|
|
1108
1112
|
}
|
|
1109
|
-
return resolved;
|
|
1113
|
+
return resolved.replace(/\\/g, "/");
|
|
1110
1114
|
}
|
|
1111
1115
|
function extractText(content) {
|
|
1112
1116
|
if (typeof content === "string")
|
|
@@ -1182,7 +1186,7 @@ function ingestClaude() {
|
|
|
1182
1186
|
}
|
|
1183
1187
|
for (const file of files) {
|
|
1184
1188
|
const sessionId = basename(file, ".jsonl");
|
|
1185
|
-
const raw =
|
|
1189
|
+
const raw = readFileSync3(join3(projPath, file), "utf-8");
|
|
1186
1190
|
const { turns, date } = parseSession(raw.split(`
|
|
1187
1191
|
`));
|
|
1188
1192
|
if (turns.length === 0) {
|
|
@@ -1191,8 +1195,7 @@ function ingestClaude() {
|
|
|
1191
1195
|
}
|
|
1192
1196
|
const header = `# Chat: ${project}${date ? ` | ${date}` : ""}`;
|
|
1193
1197
|
const body = turns.map((t) => {
|
|
1194
|
-
|
|
1195
|
-
return `## Q: ${q}
|
|
1198
|
+
return `## Q: ${t.user}
|
|
1196
1199
|
|
|
1197
1200
|
${t.assistant}`;
|
|
1198
1201
|
}).join(`
|
|
@@ -1233,103 +1236,31 @@ ${body}`;
|
|
|
1233
1236
|
}
|
|
1234
1237
|
|
|
1235
1238
|
// src/ingest/codex.ts
|
|
1236
|
-
import { existsSync as existsSync4, readFileSync as
|
|
1239
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3 } from "node:fs";
|
|
1237
1240
|
import { join as join4, basename as basename2 } from "node:path";
|
|
1238
1241
|
import { homedir as homedir4 } from "node:os";
|
|
1239
1242
|
var MAX_CONTENT2 = 48000;
|
|
1240
1243
|
function isCodexBoilerplate(text) {
|
|
1241
1244
|
return text.startsWith("# AGENTS.md instructions for") || text.startsWith("<INSTRUCTIONS>") || text.startsWith("<user_instructions>") || text.startsWith("<user_action>");
|
|
1242
1245
|
}
|
|
1243
|
-
function
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
if (cwdMatch)
|
|
1247
|
-
cwd = cwdMatch[1];
|
|
1248
|
-
const cleaned = text.replace(/<environment_context>[\s\S]*?<\/environment_context>/g, "").trim();
|
|
1249
|
-
return { text: cleaned, cwd };
|
|
1246
|
+
function extractCwd(text) {
|
|
1247
|
+
const match = text.match(/<cwd>([^<]+)<\/cwd>/);
|
|
1248
|
+
return match ? match[1] : null;
|
|
1250
1249
|
}
|
|
1251
1250
|
function projectFromCwd(cwd) {
|
|
1252
|
-
|
|
1251
|
+
const home = homedir4();
|
|
1252
|
+
const normalized = cwd.replace(/\\/g, "/");
|
|
1253
|
+
const normalizedHome = home.replace(/\\/g, "/");
|
|
1254
|
+
if (normalized.startsWith(normalizedHome + "/")) {
|
|
1255
|
+
return normalized.slice(normalizedHome.length + 1);
|
|
1256
|
+
}
|
|
1257
|
+
return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
|
|
1253
1258
|
}
|
|
1254
1259
|
function ingestCodex() {
|
|
1255
1260
|
const memories = [];
|
|
1256
1261
|
const sessionIds = [];
|
|
1257
1262
|
let filtered = 0;
|
|
1258
1263
|
let deduped = 0;
|
|
1259
|
-
const historyPath = join4(homedir4(), ".codex", "history.jsonl");
|
|
1260
|
-
if (existsSync4(historyPath)) {
|
|
1261
|
-
try {
|
|
1262
|
-
const lines = readFileSync3(historyPath, "utf-8").split(`
|
|
1263
|
-
`).filter(Boolean);
|
|
1264
|
-
const sessions = new Map;
|
|
1265
|
-
for (const line of lines) {
|
|
1266
|
-
try {
|
|
1267
|
-
const obj = JSON.parse(line);
|
|
1268
|
-
if (!obj.session_id || !obj.text)
|
|
1269
|
-
continue;
|
|
1270
|
-
if (!sessions.has(obj.session_id))
|
|
1271
|
-
sessions.set(obj.session_id, []);
|
|
1272
|
-
sessions.get(obj.session_id).push({ ts: obj.ts, text: obj.text });
|
|
1273
|
-
} catch {
|
|
1274
|
-
continue;
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
for (const [sessionId, entries] of sessions) {
|
|
1278
|
-
entries.sort((a, b) => a.ts - b.ts);
|
|
1279
|
-
const date = new Date(entries[0].ts * 1000).toISOString().slice(0, 10);
|
|
1280
|
-
let project = null;
|
|
1281
|
-
const cleanEntries = [];
|
|
1282
|
-
for (const e of entries) {
|
|
1283
|
-
let text = cleanText(e.text);
|
|
1284
|
-
if (isCodexBoilerplate(text))
|
|
1285
|
-
continue;
|
|
1286
|
-
const env = stripEnvironmentContext(text);
|
|
1287
|
-
text = env.text;
|
|
1288
|
-
if (!project && env.cwd)
|
|
1289
|
-
project = projectFromCwd(env.cwd);
|
|
1290
|
-
if (text.length === 0)
|
|
1291
|
-
continue;
|
|
1292
|
-
cleanEntries.push(text.length > 300 ? text.slice(0, 300) + "..." : text);
|
|
1293
|
-
}
|
|
1294
|
-
const body = cleanEntries.filter(Boolean).join(`
|
|
1295
|
-
|
|
1296
|
-
---
|
|
1297
|
-
|
|
1298
|
-
`);
|
|
1299
|
-
let content = `# Codex Chat | ${date}
|
|
1300
|
-
|
|
1301
|
-
${body}`;
|
|
1302
|
-
if (content.length > MAX_CONTENT2)
|
|
1303
|
-
content = content.slice(0, MAX_CONTENT2) + `
|
|
1304
|
-
|
|
1305
|
-
[truncated]`;
|
|
1306
|
-
if (content.length < 100) {
|
|
1307
|
-
filtered++;
|
|
1308
|
-
continue;
|
|
1309
|
-
}
|
|
1310
|
-
const contentHash = createContentHash(content);
|
|
1311
|
-
const dedupKey = `codex:${sessionId}`;
|
|
1312
|
-
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
1313
|
-
if (isIngested("codex", fingerprint)) {
|
|
1314
|
-
deduped++;
|
|
1315
|
-
continue;
|
|
1316
|
-
}
|
|
1317
|
-
memories.push({
|
|
1318
|
-
content,
|
|
1319
|
-
containerTag: "codex-chats",
|
|
1320
|
-
metadata: {
|
|
1321
|
-
dedupKey,
|
|
1322
|
-
contentHash,
|
|
1323
|
-
source: "codex",
|
|
1324
|
-
sessionId,
|
|
1325
|
-
date,
|
|
1326
|
-
...project ? { project } : {}
|
|
1327
|
-
}
|
|
1328
|
-
});
|
|
1329
|
-
sessionIds.push(sessionId);
|
|
1330
|
-
}
|
|
1331
|
-
} catch {}
|
|
1332
|
-
}
|
|
1333
1264
|
const sessionsDir = join4(homedir4(), ".codex", "sessions");
|
|
1334
1265
|
if (existsSync4(sessionsDir)) {
|
|
1335
1266
|
try {
|
|
@@ -1348,10 +1279,8 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
1348
1279
|
walkCodexSessions(join4(dir, entry.name), memories, sessionIds, stats);
|
|
1349
1280
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
1350
1281
|
const sessionId = basename2(entry.name, ".jsonl");
|
|
1351
|
-
if (sessionIds.includes(sessionId))
|
|
1352
|
-
continue;
|
|
1353
1282
|
try {
|
|
1354
|
-
const lines =
|
|
1283
|
+
const lines = readFileSync4(join4(dir, entry.name), "utf-8").split(`
|
|
1355
1284
|
`).filter(Boolean);
|
|
1356
1285
|
let date = null;
|
|
1357
1286
|
let project = null;
|
|
@@ -1362,19 +1291,29 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
1362
1291
|
try {
|
|
1363
1292
|
const obj = JSON.parse(line);
|
|
1364
1293
|
if (!date && obj.timestamp)
|
|
1365
|
-
date = obj.timestamp.slice(0, 10);
|
|
1294
|
+
date = typeof obj.timestamp === "string" ? obj.timestamp.slice(0, 10) : null;
|
|
1366
1295
|
if (obj.type === "session_meta" && obj.payload?.cwd) {
|
|
1367
1296
|
project = projectFromCwd(obj.payload.cwd);
|
|
1368
1297
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1298
|
+
let role;
|
|
1299
|
+
let msgContent;
|
|
1300
|
+
if (obj.type === "response_item" && obj.payload?.type === "message") {
|
|
1301
|
+
role = obj.payload.role;
|
|
1302
|
+
msgContent = Array.isArray(obj.payload.content) ? obj.payload.content : undefined;
|
|
1303
|
+
} else if (obj.type === "message" && obj.role) {
|
|
1304
|
+
role = obj.role;
|
|
1305
|
+
msgContent = Array.isArray(obj.content) ? obj.content : undefined;
|
|
1306
|
+
}
|
|
1307
|
+
if (!role || !msgContent)
|
|
1374
1308
|
continue;
|
|
1375
1309
|
if (role === "user") {
|
|
1376
1310
|
for (const block of msgContent) {
|
|
1377
1311
|
if (block.type === "input_text" && block.text) {
|
|
1312
|
+
if (!project) {
|
|
1313
|
+
const cwd = extractCwd(block.text);
|
|
1314
|
+
if (cwd)
|
|
1315
|
+
project = projectFromCwd(cwd);
|
|
1316
|
+
}
|
|
1378
1317
|
const text = cleanText(block.text);
|
|
1379
1318
|
if (isCodexBoilerplate(text))
|
|
1380
1319
|
continue;
|
|
@@ -1409,11 +1348,10 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
1409
1348
|
continue;
|
|
1410
1349
|
}
|
|
1411
1350
|
const body = rounds.map((r) => {
|
|
1412
|
-
const q = r.user.length > 300 ? r.user.slice(0, 300) + "..." : r.user;
|
|
1413
1351
|
const assistant = r.assistantParts.join(`
|
|
1414
1352
|
|
|
1415
1353
|
`);
|
|
1416
|
-
return `## Q: ${
|
|
1354
|
+
return `## Q: ${r.user}
|
|
1417
1355
|
|
|
1418
1356
|
${assistant}`;
|
|
1419
1357
|
}).join(`
|
|
@@ -1455,9 +1393,9 @@ ${body}`;
|
|
|
1455
1393
|
}
|
|
1456
1394
|
|
|
1457
1395
|
// src/ingest/cursor.ts
|
|
1458
|
-
import { readFileSync as
|
|
1396
|
+
import { readFileSync as readFileSync5, statSync } from "node:fs";
|
|
1459
1397
|
import { join as join5 } from "node:path";
|
|
1460
|
-
import { createRequire as
|
|
1398
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
1461
1399
|
var MAX_CONTENT3 = 48000;
|
|
1462
1400
|
var MAX_DB_SIZE = 2 * 1024 * 1024 * 1024;
|
|
1463
1401
|
var WARN_DB_SIZE = 500 * 1024 * 1024;
|
|
@@ -1465,10 +1403,10 @@ var MIN_TURN_LEN2 = 50;
|
|
|
1465
1403
|
function loadSqlJs(wasmDir) {
|
|
1466
1404
|
try {
|
|
1467
1405
|
if (wasmDir) {
|
|
1468
|
-
const require2 =
|
|
1406
|
+
const require2 = createRequire3(join5(wasmDir, "sql.js", "package.json"));
|
|
1469
1407
|
return require2("sql.js");
|
|
1470
1408
|
} else {
|
|
1471
|
-
const require2 =
|
|
1409
|
+
const require2 = createRequire3(import.meta.url);
|
|
1472
1410
|
return require2("sql.js");
|
|
1473
1411
|
}
|
|
1474
1412
|
} catch {
|
|
@@ -1481,7 +1419,7 @@ function openDb(initSqlJs, dbPath, wasmDir) {
|
|
|
1481
1419
|
locateOpts.locateFile = (file) => join5(wasmDir, "sql.js", "dist", file);
|
|
1482
1420
|
}
|
|
1483
1421
|
return initSqlJs(locateOpts).then((SQL) => {
|
|
1484
|
-
const buffer =
|
|
1422
|
+
const buffer = readFileSync5(dbPath);
|
|
1485
1423
|
return new SQL.Database(buffer);
|
|
1486
1424
|
});
|
|
1487
1425
|
}
|
|
@@ -1575,8 +1513,7 @@ async function ingestCursor(dbPath, wasmDir) {
|
|
|
1575
1513
|
const date = parsed.createdAt ? new Date(parsed.createdAt).toISOString().slice(0, 10) : "unknown";
|
|
1576
1514
|
const header = `# ${sessionName} | ${date}`;
|
|
1577
1515
|
const body = turns.map((t) => {
|
|
1578
|
-
|
|
1579
|
-
return `## Q: ${q}
|
|
1516
|
+
return `## Q: ${t.user}
|
|
1580
1517
|
|
|
1581
1518
|
${t.assistant}`;
|
|
1582
1519
|
}).join(`
|
|
@@ -1622,7 +1559,7 @@ ${body}`;
|
|
|
1622
1559
|
|
|
1623
1560
|
// src/ingest/codebase.ts
|
|
1624
1561
|
import { createHash as createHash2 } from "node:crypto";
|
|
1625
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
1562
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync2, existsSync as existsSync5 } from "node:fs";
|
|
1626
1563
|
import { join as join6, relative, extname, resolve } from "node:path";
|
|
1627
1564
|
var DEFAULT_IGNORE = new Set([
|
|
1628
1565
|
"node_modules",
|
|
@@ -1712,7 +1649,7 @@ function parseGitignore(rootPath) {
|
|
|
1712
1649
|
if (!existsSync5(gitignorePath))
|
|
1713
1650
|
return patterns;
|
|
1714
1651
|
try {
|
|
1715
|
-
const content =
|
|
1652
|
+
const content = readFileSync6(gitignorePath, "utf-8");
|
|
1716
1653
|
for (const line of content.split(`
|
|
1717
1654
|
`)) {
|
|
1718
1655
|
const trimmed = line.trim();
|
|
@@ -1825,7 +1762,7 @@ function indexCodebase(rootPath) {
|
|
|
1825
1762
|
}
|
|
1826
1763
|
let raw;
|
|
1827
1764
|
try {
|
|
1828
|
-
raw =
|
|
1765
|
+
raw = readFileSync6(fullPath, "utf-8");
|
|
1829
1766
|
} catch {
|
|
1830
1767
|
skipped++;
|
|
1831
1768
|
continue;
|
|
@@ -1995,10 +1932,10 @@ async function uploadBulk(apiKey, memories, onProgress) {
|
|
|
1995
1932
|
}
|
|
1996
1933
|
|
|
1997
1934
|
// src/configure.ts
|
|
1998
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as
|
|
1935
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1999
1936
|
import { dirname, join as join7 } from "node:path";
|
|
2000
|
-
import { homedir as homedir5 } from "node:os";
|
|
2001
|
-
import { spawnSync
|
|
1937
|
+
import { homedir as homedir5, platform as platform4 } from "node:os";
|
|
1938
|
+
import { spawnSync } from "node:child_process";
|
|
2002
1939
|
var CONARE_URL = "https://mcp.conare.ai";
|
|
2003
1940
|
var SERVER_NAME = "conare-memory";
|
|
2004
1941
|
var MCP_TARGETS = [
|
|
@@ -2009,7 +1946,7 @@ var MCP_TARGETS = [
|
|
|
2009
1946
|
];
|
|
2010
1947
|
function readJsonFile(path) {
|
|
2011
1948
|
try {
|
|
2012
|
-
return JSON.parse(
|
|
1949
|
+
return JSON.parse(readFileSync7(path, "utf-8"));
|
|
2013
1950
|
} catch {
|
|
2014
1951
|
return {};
|
|
2015
1952
|
}
|
|
@@ -2039,8 +1976,9 @@ function upsertMcpServer(path, apiKey) {
|
|
|
2039
1976
|
function configureClaude(apiKey) {
|
|
2040
1977
|
const claudeConfigPath = join7(homedir5(), ".claude.json");
|
|
2041
1978
|
const claudeMcpPath = join7(homedir5(), ".claude", "mcp.json");
|
|
2042
|
-
if (
|
|
2043
|
-
stdio: "ignore"
|
|
1979
|
+
if (spawnSync("claude", ["mcp", "add-json", SERVER_NAME, "--scope", "user", JSON.stringify(getServerConfig(apiKey))], {
|
|
1980
|
+
stdio: "ignore",
|
|
1981
|
+
shell: platform4() === "win32"
|
|
2044
1982
|
}).status === 0) {
|
|
2045
1983
|
return "Claude Code configured via `claude mcp add-json`";
|
|
2046
1984
|
}
|
|
@@ -2072,7 +2010,7 @@ function configureMcp(apiKey, targets = ["claude", "cursor", "codex"]) {
|
|
|
2072
2010
|
}
|
|
2073
2011
|
|
|
2074
2012
|
// src/config.ts
|
|
2075
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as
|
|
2013
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2076
2014
|
import { join as join8 } from "node:path";
|
|
2077
2015
|
import { homedir as homedir6 } from "node:os";
|
|
2078
2016
|
var CONFIG_DIR = join8(homedir6(), ".conare");
|
|
@@ -2081,7 +2019,7 @@ function readConfig() {
|
|
|
2081
2019
|
try {
|
|
2082
2020
|
if (!existsSync7(CONFIG_PATH))
|
|
2083
2021
|
return {};
|
|
2084
|
-
return JSON.parse(
|
|
2022
|
+
return JSON.parse(readFileSync8(CONFIG_PATH, "utf-8"));
|
|
2085
2023
|
} catch {
|
|
2086
2024
|
return {};
|
|
2087
2025
|
}
|
|
@@ -2096,9 +2034,9 @@ function getSavedApiKey() {
|
|
|
2096
2034
|
}
|
|
2097
2035
|
|
|
2098
2036
|
// src/sync.ts
|
|
2099
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as
|
|
2037
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync9, chmodSync, cpSync, rmSync, symlinkSync, readlinkSync, appendFileSync } from "node:fs";
|
|
2100
2038
|
import { join as join9, dirname as dirname2 } from "node:path";
|
|
2101
|
-
import { homedir as homedir7, platform as
|
|
2039
|
+
import { homedir as homedir7, platform as platform5 } from "node:os";
|
|
2102
2040
|
import { execSync } from "node:child_process";
|
|
2103
2041
|
var CONARE_DIR = join9(homedir7(), ".conare");
|
|
2104
2042
|
var BIN_DIR = join9(CONARE_DIR, "bin");
|
|
@@ -2108,6 +2046,40 @@ var PLIST_PATH = join9(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.pl
|
|
|
2108
2046
|
var SYSTEMD_DIR = join9(homedir7(), ".config", "systemd", "user");
|
|
2109
2047
|
var SYSTEMD_SERVICE = join9(SYSTEMD_DIR, "conare-sync.service");
|
|
2110
2048
|
var SYSTEMD_TIMER = join9(SYSTEMD_DIR, "conare-sync.timer");
|
|
2049
|
+
var TASK_NAME = "ConareMemorySync";
|
|
2050
|
+
var RUN_CMD = `@echo off
|
|
2051
|
+
REM Conare Memory — background sync wrapper (Windows)
|
|
2052
|
+
setlocal
|
|
2053
|
+
|
|
2054
|
+
set "CONARE_DIR=%USERPROFILE%\\.conare"
|
|
2055
|
+
set "LOG=%CONARE_DIR%\\ingest.log"
|
|
2056
|
+
set "LOCKFILE=%CONARE_DIR%\\sync.lock.d"
|
|
2057
|
+
|
|
2058
|
+
REM File lock: prevent concurrent runs
|
|
2059
|
+
if exist "%LOCKFILE%" (
|
|
2060
|
+
echo %DATE% %TIME% SKIP: another instance running >> "%LOG%"
|
|
2061
|
+
exit /b 0
|
|
2062
|
+
)
|
|
2063
|
+
mkdir "%LOCKFILE%" 2>nul
|
|
2064
|
+
if errorlevel 1 (
|
|
2065
|
+
echo %DATE% %TIME% SKIP: lock failed >> "%LOG%"
|
|
2066
|
+
exit /b 0
|
|
2067
|
+
)
|
|
2068
|
+
|
|
2069
|
+
REM Resolve node
|
|
2070
|
+
where node >nul 2>nul
|
|
2071
|
+
if errorlevel 1 (
|
|
2072
|
+
echo %DATE% %TIME% ERROR: node not found >> "%LOG%"
|
|
2073
|
+
rmdir "%LOCKFILE%" 2>nul
|
|
2074
|
+
exit /b 1
|
|
2075
|
+
)
|
|
2076
|
+
|
|
2077
|
+
echo %DATE% %TIME% START sync >> "%LOG%"
|
|
2078
|
+
node "%CONARE_DIR%\\bin\\conare-ingest.mjs" --config-file "%CONARE_DIR%\\config.json" --ingest-only --quiet 2>> "%LOG%"
|
|
2079
|
+
echo %DATE% %TIME% DONE sync (exit %ERRORLEVEL%) >> "%LOG%"
|
|
2080
|
+
|
|
2081
|
+
rmdir "%LOCKFILE%" 2>nul
|
|
2082
|
+
`;
|
|
2111
2083
|
var RUN_SH = `#!/bin/bash
|
|
2112
2084
|
# Conare Memory — background sync wrapper
|
|
2113
2085
|
# Resolves node at runtime to survive nvm/fnm upgrades
|
|
@@ -2238,7 +2210,7 @@ function persistBinary(apiKey) {
|
|
|
2238
2210
|
throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
|
|
2239
2211
|
}
|
|
2240
2212
|
const dest = join9(BIN_DIR, "conare-ingest.mjs");
|
|
2241
|
-
const content =
|
|
2213
|
+
const content = readFileSync9(cliEntry, "utf-8");
|
|
2242
2214
|
writeFileSync4(dest, content);
|
|
2243
2215
|
const sqlJsDir = findSqlJs();
|
|
2244
2216
|
if (sqlJsDir) {
|
|
@@ -2250,7 +2222,11 @@ function persistBinary(apiKey) {
|
|
|
2250
2222
|
}
|
|
2251
2223
|
const runShPath = join9(BIN_DIR, "run.sh");
|
|
2252
2224
|
writeFileSync4(runShPath, RUN_SH);
|
|
2253
|
-
|
|
2225
|
+
try {
|
|
2226
|
+
chmodSync(runShPath, 493);
|
|
2227
|
+
} catch {}
|
|
2228
|
+
const runCmdPath = join9(BIN_DIR, "run.cmd");
|
|
2229
|
+
writeFileSync4(runCmdPath, RUN_CMD);
|
|
2254
2230
|
writeFileSync4(CONFIG_PATH2, JSON.stringify({ apiKey }, null, 2) + `
|
|
2255
2231
|
`);
|
|
2256
2232
|
}
|
|
@@ -2323,6 +2299,24 @@ function setupLinuxCron(intervalMinutes) {
|
|
|
2323
2299
|
}
|
|
2324
2300
|
}
|
|
2325
2301
|
function installGlobalCommand() {
|
|
2302
|
+
const isWindows = platform5() === "win32";
|
|
2303
|
+
if (isWindows) {
|
|
2304
|
+
const wrapper2 = join9(BIN_DIR, "conare.cmd");
|
|
2305
|
+
const content2 = `@echo off\r
|
|
2306
|
+
node "%USERPROFILE%\\.conare\\bin\\conare-ingest.mjs" %*\r
|
|
2307
|
+
`;
|
|
2308
|
+
writeFileSync4(wrapper2, content2);
|
|
2309
|
+
const pathDirs = (process.env.PATH || "").split(";");
|
|
2310
|
+
if (pathDirs.some((d) => d.toLowerCase() === BIN_DIR.toLowerCase())) {
|
|
2311
|
+
return "Global command: conare (via .conare\\bin in PATH)";
|
|
2312
|
+
}
|
|
2313
|
+
try {
|
|
2314
|
+
execSync(`powershell -NoProfile -Command "[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH','User') + ';${BIN_DIR}', 'User')"`, { stdio: "ignore" });
|
|
2315
|
+
return `Global command: conare (added .conare\\bin to user PATH — restart terminal)`;
|
|
2316
|
+
} catch {
|
|
2317
|
+
return `Global command: add ${BIN_DIR} to your PATH manually`;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2326
2320
|
const wrapper = join9(BIN_DIR, "conare");
|
|
2327
2321
|
const content = `#!/bin/bash
|
|
2328
2322
|
# Conare global command — runs the persisted CLI bundle
|
|
@@ -2362,7 +2356,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
|
|
|
2362
2356
|
const shellProfile = getShellProfile();
|
|
2363
2357
|
if (shellProfile) {
|
|
2364
2358
|
try {
|
|
2365
|
-
const profileContent = existsSync8(shellProfile) ?
|
|
2359
|
+
const profileContent = existsSync8(shellProfile) ? readFileSync9(shellProfile, "utf-8") : "";
|
|
2366
2360
|
const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
|
|
2367
2361
|
if (!profileContent.includes(".conare/bin")) {
|
|
2368
2362
|
appendFileSync(shellProfile, `
|
|
@@ -2386,7 +2380,7 @@ function getShellProfile() {
|
|
|
2386
2380
|
return join9(home, ".zshrc");
|
|
2387
2381
|
if (shell.includes("bash")) {
|
|
2388
2382
|
const profile = join9(home, ".bash_profile");
|
|
2389
|
-
if (
|
|
2383
|
+
if (platform5() === "darwin" && existsSync8(profile))
|
|
2390
2384
|
return profile;
|
|
2391
2385
|
return join9(home, ".bashrc");
|
|
2392
2386
|
}
|
|
@@ -2396,9 +2390,16 @@ function getShellProfile() {
|
|
|
2396
2390
|
return join9(home, ".bashrc");
|
|
2397
2391
|
return null;
|
|
2398
2392
|
}
|
|
2393
|
+
function setupWindows(intervalMinutes) {
|
|
2394
|
+
const runCmd = join9(BIN_DIR, "run.cmd").replace(/\//g, "\\");
|
|
2395
|
+
try {
|
|
2396
|
+
execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
2397
|
+
} catch {}
|
|
2398
|
+
execSync(`schtasks /Create /TN "${TASK_NAME}" /TR "${runCmd}" /SC MINUTE /MO ${intervalMinutes} /F`, { stdio: "ignore" });
|
|
2399
|
+
}
|
|
2399
2400
|
function installSync(apiKey, intervalMinutes = 10) {
|
|
2400
2401
|
const messages = [];
|
|
2401
|
-
const os =
|
|
2402
|
+
const os = platform5();
|
|
2402
2403
|
persistBinary(apiKey);
|
|
2403
2404
|
messages.push("Persisted CLI to ~/.conare/bin/");
|
|
2404
2405
|
messages.push("Saved config to ~/.conare/config.json");
|
|
@@ -2409,6 +2410,9 @@ function installSync(apiKey, intervalMinutes = 10) {
|
|
|
2409
2410
|
setupMacOS(intervalMinutes);
|
|
2410
2411
|
messages.push(`Installed launchd agent (every ${intervalMinutes} min)`);
|
|
2411
2412
|
messages.push(`Plist: ${PLIST_PATH}`);
|
|
2413
|
+
} else if (os === "win32") {
|
|
2414
|
+
setupWindows(intervalMinutes);
|
|
2415
|
+
messages.push(`Installed Windows Task Scheduler (every ${intervalMinutes} min)`);
|
|
2412
2416
|
} else if (os === "linux") {
|
|
2413
2417
|
if (hasSystemd()) {
|
|
2414
2418
|
setupLinuxSystemd(intervalMinutes);
|
|
@@ -2424,7 +2428,7 @@ function installSync(apiKey, intervalMinutes = 10) {
|
|
|
2424
2428
|
}
|
|
2425
2429
|
function uninstallSync() {
|
|
2426
2430
|
const messages = [];
|
|
2427
|
-
const os =
|
|
2431
|
+
const os = platform5();
|
|
2428
2432
|
if (os === "darwin") {
|
|
2429
2433
|
try {
|
|
2430
2434
|
execSync(`launchctl bootout gui/${uid()} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
@@ -2433,6 +2437,11 @@ function uninstallSync() {
|
|
|
2433
2437
|
unlinkSync(PLIST_PATH);
|
|
2434
2438
|
messages.push("Removed launchd agent");
|
|
2435
2439
|
}
|
|
2440
|
+
} else if (os === "win32") {
|
|
2441
|
+
try {
|
|
2442
|
+
execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
2443
|
+
messages.push("Removed Windows scheduled task");
|
|
2444
|
+
} catch {}
|
|
2436
2445
|
} else if (os === "linux") {
|
|
2437
2446
|
if (hasSystemd()) {
|
|
2438
2447
|
try {
|
|
@@ -2658,8 +2667,8 @@ async function main() {
|
|
|
2658
2667
|
let configFileKey;
|
|
2659
2668
|
if (opts.configFile) {
|
|
2660
2669
|
try {
|
|
2661
|
-
const { readFileSync:
|
|
2662
|
-
const raw = JSON.parse(
|
|
2670
|
+
const { readFileSync: readFileSync10 } = await import("node:fs");
|
|
2671
|
+
const raw = JSON.parse(readFileSync10(opts.configFile, "utf-8"));
|
|
2663
2672
|
configFileKey = raw.apiKey || raw.key;
|
|
2664
2673
|
if (!configFileKey) {
|
|
2665
2674
|
console.error(`Error: no apiKey/key found in ${opts.configFile}`);
|
|
@@ -2691,7 +2700,7 @@ async function main() {
|
|
|
2691
2700
|
}));
|
|
2692
2701
|
let interactiveMode = false;
|
|
2693
2702
|
if (shouldRunInteractive) {
|
|
2694
|
-
const detectedTools = detect();
|
|
2703
|
+
const detectedTools = await detect();
|
|
2695
2704
|
interactiveTargets = MCP_TARGETS.map((target) => {
|
|
2696
2705
|
const detected = detectedTools.find((tool) => target.id === "claude" && tool.name === "Claude Code" || target.id === "cursor" && tool.name === "Cursor" || target.id === "codex" && tool.name === "Codex" || target.id === "openclaw" && tool.name === "OpenClaw");
|
|
2697
2706
|
return {
|
|
@@ -2828,7 +2837,7 @@ Nothing new to index.`);
|
|
|
2828
2837
|
}
|
|
2829
2838
|
log();
|
|
2830
2839
|
}
|
|
2831
|
-
const tools = detect();
|
|
2840
|
+
const tools = await detect();
|
|
2832
2841
|
if (!interactiveMode) {
|
|
2833
2842
|
log("Detected AI tools:");
|
|
2834
2843
|
for (const t of tools) {
|