conare 0.0.9 → 0.1.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/dist/index.js +112 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -281,6 +281,7 @@ import { homedir as homedir3 } from "node:os";
|
|
|
281
281
|
|
|
282
282
|
// src/ingest/shared.ts
|
|
283
283
|
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
284
|
+
import { createHash } from "node:crypto";
|
|
284
285
|
import { join as join2 } from "node:path";
|
|
285
286
|
import { homedir as homedir2 } from "node:os";
|
|
286
287
|
var MANIFEST_PATH = join2(homedir2(), ".conare", "ingested.json");
|
|
@@ -290,6 +291,9 @@ function cleanText(raw) {
|
|
|
290
291
|
text = text.replace(/<attached-context[\s\S]*?<\/attached-context>/g, "");
|
|
291
292
|
return text.trim();
|
|
292
293
|
}
|
|
294
|
+
function createContentHash(content) {
|
|
295
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
296
|
+
}
|
|
293
297
|
function getIngested() {
|
|
294
298
|
try {
|
|
295
299
|
if (existsSync2(MANIFEST_PATH)) {
|
|
@@ -390,10 +394,6 @@ function ingestClaude() {
|
|
|
390
394
|
}
|
|
391
395
|
for (const file of files) {
|
|
392
396
|
const sessionId = basename(file, ".jsonl");
|
|
393
|
-
if (isIngested("claude", sessionId)) {
|
|
394
|
-
skipped++;
|
|
395
|
-
continue;
|
|
396
|
-
}
|
|
397
397
|
const raw = readFileSync2(join3(projPath, file), "utf-8");
|
|
398
398
|
const { turns, date } = parseSession(raw.split(`
|
|
399
399
|
`));
|
|
@@ -419,10 +419,24 @@ ${body}`;
|
|
|
419
419
|
content = content.slice(0, MAX_CONTENT) + `
|
|
420
420
|
|
|
421
421
|
[truncated]`;
|
|
422
|
+
const contentHash = createContentHash(content);
|
|
423
|
+
const dedupKey = `claude:${sessionId}`;
|
|
424
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
425
|
+
if (isIngested("claude", fingerprint)) {
|
|
426
|
+
skipped++;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
422
429
|
memories.push({
|
|
423
430
|
content,
|
|
424
431
|
containerTag: "claude-chats",
|
|
425
|
-
metadata: {
|
|
432
|
+
metadata: {
|
|
433
|
+
dedupKey,
|
|
434
|
+
contentHash,
|
|
435
|
+
source: "claude-code",
|
|
436
|
+
sessionId,
|
|
437
|
+
project,
|
|
438
|
+
date: date || "unknown"
|
|
439
|
+
}
|
|
426
440
|
});
|
|
427
441
|
sessionIds.push(sessionId);
|
|
428
442
|
}
|
|
@@ -458,10 +472,6 @@ function ingestCodex() {
|
|
|
458
472
|
}
|
|
459
473
|
}
|
|
460
474
|
for (const [sessionId, entries] of sessions) {
|
|
461
|
-
if (isIngested("codex", sessionId)) {
|
|
462
|
-
skipped++;
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
475
|
entries.sort((a, b) => a.ts - b.ts);
|
|
466
476
|
const date = new Date(entries[0].ts * 1000).toISOString().slice(0, 10);
|
|
467
477
|
const body = entries.map((e) => {
|
|
@@ -483,10 +493,17 @@ ${body}`;
|
|
|
483
493
|
skipped++;
|
|
484
494
|
continue;
|
|
485
495
|
}
|
|
496
|
+
const contentHash = createContentHash(content);
|
|
497
|
+
const dedupKey = `codex:${sessionId}`;
|
|
498
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
499
|
+
if (isIngested("codex", fingerprint)) {
|
|
500
|
+
skipped++;
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
486
503
|
memories.push({
|
|
487
504
|
content,
|
|
488
505
|
containerTag: "codex-chats",
|
|
489
|
-
metadata: { source: "codex", sessionId, date }
|
|
506
|
+
metadata: { dedupKey, contentHash, source: "codex", sessionId, date }
|
|
490
507
|
});
|
|
491
508
|
sessionIds.push(sessionId);
|
|
492
509
|
}
|
|
@@ -507,7 +524,7 @@ function walkCodexSessions(dir, memories, sessionIds, skipped) {
|
|
|
507
524
|
walkCodexSessions(join4(dir, entry.name), memories, sessionIds, skipped);
|
|
508
525
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
509
526
|
const sessionId = basename2(entry.name, ".jsonl");
|
|
510
|
-
if (
|
|
527
|
+
if (sessionIds.includes(sessionId))
|
|
511
528
|
continue;
|
|
512
529
|
try {
|
|
513
530
|
const lines = readFileSync3(join4(dir, entry.name), "utf-8").split(`
|
|
@@ -549,10 +566,22 @@ ${body}`;
|
|
|
549
566
|
content = content.slice(0, MAX_CONTENT2) + `
|
|
550
567
|
|
|
551
568
|
[truncated]`;
|
|
569
|
+
const contentHash = createContentHash(content);
|
|
570
|
+
const dedupKey = `codex:${sessionId}`;
|
|
571
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
572
|
+
if (isIngested("codex", fingerprint)) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
552
575
|
memories.push({
|
|
553
576
|
content,
|
|
554
577
|
containerTag: "codex-chats",
|
|
555
|
-
metadata: {
|
|
578
|
+
metadata: {
|
|
579
|
+
dedupKey,
|
|
580
|
+
contentHash,
|
|
581
|
+
source: "codex-session",
|
|
582
|
+
sessionId,
|
|
583
|
+
date: date || "unknown"
|
|
584
|
+
}
|
|
556
585
|
});
|
|
557
586
|
sessionIds.push(sessionId);
|
|
558
587
|
} catch {}
|
|
@@ -656,10 +685,6 @@ async function ingestCursor(dbPath, wasmDir) {
|
|
|
656
685
|
} catch {}
|
|
657
686
|
for (const [key, value] of rows) {
|
|
658
687
|
const composerId = key.replace("composerData:", "");
|
|
659
|
-
if (isIngested("cursor", composerId)) {
|
|
660
|
-
skipped++;
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
688
|
let parsed;
|
|
664
689
|
try {
|
|
665
690
|
parsed = JSON.parse(value);
|
|
@@ -701,10 +726,24 @@ ${body}`;
|
|
|
701
726
|
content = content.slice(0, MAX_CONTENT3) + `
|
|
702
727
|
|
|
703
728
|
[truncated]`;
|
|
729
|
+
const contentHash = createContentHash(content);
|
|
730
|
+
const dedupKey = `cursor:${composerId}`;
|
|
731
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
732
|
+
if (isIngested("cursor", fingerprint)) {
|
|
733
|
+
skipped++;
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
704
736
|
memories.push({
|
|
705
737
|
content,
|
|
706
738
|
containerTag: "cursor-chats",
|
|
707
|
-
metadata: {
|
|
739
|
+
metadata: {
|
|
740
|
+
dedupKey,
|
|
741
|
+
contentHash,
|
|
742
|
+
source: "cursor",
|
|
743
|
+
sessionId: composerId,
|
|
744
|
+
name: sessionName,
|
|
745
|
+
date
|
|
746
|
+
}
|
|
708
747
|
});
|
|
709
748
|
sessionIds.push(composerId);
|
|
710
749
|
}
|
|
@@ -717,7 +756,7 @@ ${body}`;
|
|
|
717
756
|
}
|
|
718
757
|
|
|
719
758
|
// src/ingest/codebase.ts
|
|
720
|
-
import { createHash } from "node:crypto";
|
|
759
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
721
760
|
import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync2, existsSync as existsSync4 } from "node:fs";
|
|
722
761
|
import { join as join6, relative, extname, resolve } from "node:path";
|
|
723
762
|
var DEFAULT_IGNORE = new Set([
|
|
@@ -884,6 +923,7 @@ ${content}
|
|
|
884
923
|
}
|
|
885
924
|
function indexCodebase(rootPath) {
|
|
886
925
|
const absRoot = resolve(rootPath);
|
|
926
|
+
const repoHash = createHash2("sha256").update(absRoot).digest("hex").slice(0, 12);
|
|
887
927
|
const gitignorePatterns = parseGitignore(absRoot);
|
|
888
928
|
const memories = [];
|
|
889
929
|
let fileCount = 0;
|
|
@@ -930,9 +970,10 @@ function indexCodebase(rootPath) {
|
|
|
930
970
|
continue;
|
|
931
971
|
}
|
|
932
972
|
const relPath = relative(absRoot, fullPath);
|
|
933
|
-
const contentHash =
|
|
934
|
-
const
|
|
935
|
-
|
|
973
|
+
const contentHash = createContentHash(raw);
|
|
974
|
+
const dedupKey = `codebase:${repoHash}:${relPath}`;
|
|
975
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
976
|
+
if (isIngested("codebase", fingerprint)) {
|
|
936
977
|
skipped++;
|
|
937
978
|
continue;
|
|
938
979
|
}
|
|
@@ -941,9 +982,12 @@ function indexCodebase(rootPath) {
|
|
|
941
982
|
content,
|
|
942
983
|
containerTag: "codebase",
|
|
943
984
|
metadata: {
|
|
985
|
+
dedupKey,
|
|
986
|
+
contentHash,
|
|
944
987
|
source: "codebase-index",
|
|
988
|
+
repoHash,
|
|
945
989
|
filePath: relPath,
|
|
946
|
-
fileHash
|
|
990
|
+
fileHash: `${relPath}:${contentHash}`,
|
|
947
991
|
language: langFromExt(ext)
|
|
948
992
|
}
|
|
949
993
|
});
|
|
@@ -992,6 +1036,21 @@ async function validateKey(apiKey) {
|
|
|
992
1036
|
return { valid: false };
|
|
993
1037
|
}
|
|
994
1038
|
}
|
|
1039
|
+
async function getRemoteMemoryCount(apiKey) {
|
|
1040
|
+
try {
|
|
1041
|
+
const res = await fetch(`${API_URL}/api/containers`, {
|
|
1042
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1043
|
+
});
|
|
1044
|
+
if (!res.ok)
|
|
1045
|
+
return null;
|
|
1046
|
+
const data = await res.json();
|
|
1047
|
+
if (!Array.isArray(data.containers))
|
|
1048
|
+
return 0;
|
|
1049
|
+
return data.containers.reduce((sum, container) => sum + (container.count || 0), 0);
|
|
1050
|
+
} catch {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
995
1054
|
async function uploadItems(apiKey, items) {
|
|
996
1055
|
let retries = 4;
|
|
997
1056
|
while (retries > 0) {
|
|
@@ -1822,9 +1881,12 @@ async function confirmIndexCodebase() {
|
|
|
1822
1881
|
}
|
|
1823
1882
|
|
|
1824
1883
|
// src/index.ts
|
|
1825
|
-
function
|
|
1884
|
+
function getManifestFingerprint(memory) {
|
|
1826
1885
|
const metadata = memory.metadata;
|
|
1827
|
-
|
|
1886
|
+
if (metadata?.dedupKey && metadata?.contentHash) {
|
|
1887
|
+
return `${metadata.dedupKey}:${metadata.contentHash}`;
|
|
1888
|
+
}
|
|
1889
|
+
return metadata?.dedupKey || metadata?.sessionId || metadata?.fileHash || null;
|
|
1828
1890
|
}
|
|
1829
1891
|
function printMissingKeyError() {
|
|
1830
1892
|
console.error("Error: no API key configured.");
|
|
@@ -1854,6 +1916,16 @@ function printFailureSummary(results, memories) {
|
|
|
1854
1916
|
console.log(` - ... and ${failures.length - 3} more`);
|
|
1855
1917
|
}
|
|
1856
1918
|
}
|
|
1919
|
+
function renderProgressSummary(success, failed, noun) {
|
|
1920
|
+
const total = success + failed;
|
|
1921
|
+
const barWidth = 20;
|
|
1922
|
+
const ratio = total === 0 ? 1 : success / total;
|
|
1923
|
+
const pct = (ratio * 100).toFixed(1);
|
|
1924
|
+
const filled = Math.round(ratio * barWidth);
|
|
1925
|
+
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
1926
|
+
return `\r \x1B[32m✓\x1B[0m [\x1B[36m${bar}\x1B[0m] ${success} ${noun}, ${failed} failed (${pct}%)${" ".repeat(12)}
|
|
1927
|
+
`;
|
|
1928
|
+
}
|
|
1857
1929
|
function parseArgs() {
|
|
1858
1930
|
const args = process.argv.slice(2);
|
|
1859
1931
|
let key = "";
|
|
@@ -1977,6 +2049,16 @@ async function main() {
|
|
|
1977
2049
|
console.log(auth.email ? `OK (${auth.email})` : "OK");
|
|
1978
2050
|
saveApiKey(apiKey);
|
|
1979
2051
|
console.log();
|
|
2052
|
+
if (!opts.force && !opts.dryRun) {
|
|
2053
|
+
const remoteMemoryCount = await getRemoteMemoryCount(apiKey);
|
|
2054
|
+
const localManifest = getIngested();
|
|
2055
|
+
const localManifestCount = Object.values(localManifest).reduce((sum, entries) => sum + entries.length, 0);
|
|
2056
|
+
if (remoteMemoryCount === 0 && localManifestCount > 0) {
|
|
2057
|
+
clearIngested();
|
|
2058
|
+
console.log("Remote account is empty; cleared stale local ingestion history.");
|
|
2059
|
+
console.log("");
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
1980
2062
|
if (!opts.wasmDir && existsSync7(join9(process.cwd(), "node_modules", "sql.js"))) {
|
|
1981
2063
|
opts.wasmDir = join9(process.cwd(), "node_modules");
|
|
1982
2064
|
}
|
|
@@ -2035,9 +2117,8 @@ Nothing new to index.`);
|
|
|
2035
2117
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
2036
2118
|
process.stdout.write(`\r Uploading [\x1B[36m${bar}\x1B[0m] ${uploaded}/${total} (${pct}%)`);
|
|
2037
2119
|
});
|
|
2038
|
-
process.stdout.write(
|
|
2039
|
-
|
|
2040
|
-
const fileHashes = results.filter((result) => result.success).map((result) => getDedupKey(memories[result.index])).filter((key) => !!key);
|
|
2120
|
+
process.stdout.write(renderProgressSummary(success, failed, "indexed"));
|
|
2121
|
+
const fileHashes = results.filter((result) => result.success).map((result) => getManifestFingerprint(memories[result.index])).filter((key) => !!key);
|
|
2041
2122
|
markIngested("codebase", fileHashes);
|
|
2042
2123
|
printFailureSummary(results, memories);
|
|
2043
2124
|
}
|
|
@@ -2114,8 +2195,7 @@ Nothing new to index.`);
|
|
|
2114
2195
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
2115
2196
|
process.stdout.write(`\r Uploading [\x1B[33m${bar}\x1B[0m] ${uploaded}/${total} (${pct}%)`);
|
|
2116
2197
|
});
|
|
2117
|
-
process.stdout.write(
|
|
2118
|
-
`);
|
|
2198
|
+
process.stdout.write(renderProgressSummary(success, failed, "uploaded"));
|
|
2119
2199
|
if (failed > 0) {
|
|
2120
2200
|
console.log(` Re-run with --force to retry failed memories.`);
|
|
2121
2201
|
}
|
|
@@ -2129,7 +2209,7 @@ Nothing new to index.`);
|
|
|
2129
2209
|
if (!result.success)
|
|
2130
2210
|
continue;
|
|
2131
2211
|
const memory = allMemories[result.index];
|
|
2132
|
-
const key =
|
|
2212
|
+
const key = getManifestFingerprint(memory);
|
|
2133
2213
|
if (!key)
|
|
2134
2214
|
continue;
|
|
2135
2215
|
switch (memory.containerTag) {
|
|
@@ -2191,9 +2271,8 @@ Nothing new to index.`);
|
|
|
2191
2271
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
2192
2272
|
process.stdout.write(`\r Uploading [\x1B[36m${bar}\x1B[0m] ${uploaded}/${total} (${pct}%)`);
|
|
2193
2273
|
});
|
|
2194
|
-
process.stdout.write(
|
|
2195
|
-
|
|
2196
|
-
const fileHashes = results.filter((result) => result.success).map((result) => getDedupKey(memories[result.index])).filter((key) => !!key);
|
|
2274
|
+
process.stdout.write(renderProgressSummary(success, failed, "indexed"));
|
|
2275
|
+
const fileHashes = results.filter((result) => result.success).map((result) => getManifestFingerprint(memories[result.index])).filter((key) => !!key);
|
|
2197
2276
|
markIngested("codebase", fileHashes);
|
|
2198
2277
|
printFailureSummary(results, memories);
|
|
2199
2278
|
}
|