conare 0.4.4 → 0.4.5
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 +124 -126
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,6 +34,27 @@ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync
|
|
|
34
34
|
import { createHash } from "node:crypto";
|
|
35
35
|
import { join as join2 } from "node:path";
|
|
36
36
|
import { homedir as homedir2 } from "node:os";
|
|
37
|
+
function fitContent(header, rounds) {
|
|
38
|
+
const buildContent = (maxUser) => {
|
|
39
|
+
const body = rounds.map((r) => {
|
|
40
|
+
const user = maxUser > 0 && r.user.length > maxUser ? r.user.slice(0, maxUser) + "..." : r.user;
|
|
41
|
+
return `## Q: ${user}
|
|
42
|
+
|
|
43
|
+
${r.assistant}`;
|
|
44
|
+
}).join(`
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
`);
|
|
49
|
+
return `${header}
|
|
50
|
+
|
|
51
|
+
${body}`;
|
|
52
|
+
};
|
|
53
|
+
const full = buildContent(0);
|
|
54
|
+
if (full.length <= MAX_MEMORY_CONTENT)
|
|
55
|
+
return full;
|
|
56
|
+
return buildContent(TRUNCATED_USER_MSG);
|
|
57
|
+
}
|
|
37
58
|
function isNarration(text) {
|
|
38
59
|
const stripped = text.trim();
|
|
39
60
|
if (stripped.length < MIN_SUBSTANTIVE)
|
|
@@ -97,7 +118,7 @@ function clearIngested(source) {
|
|
|
97
118
|
mkdirSync(dir, { recursive: true });
|
|
98
119
|
writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
99
120
|
}
|
|
100
|
-
var MANIFEST_PATH, MIN_SUBSTANTIVE = 200, NARRATION_RE;
|
|
121
|
+
var MANIFEST_PATH, MAX_MEMORY_CONTENT = 200000, TRUNCATED_USER_MSG = 3000, MIN_SUBSTANTIVE = 200, NARRATION_RE;
|
|
101
122
|
var init_shared = __esm(() => {
|
|
102
123
|
MANIFEST_PATH = join2(homedir2(), ".conare", "ingested.json");
|
|
103
124
|
NARRATION_RE = /^[\s\n]*(Let me |Now let me |Now I['\u2019]|Now add |Now fix |Now replace |Now integrate |Now update |Now pass |Now clean |Now build|Update the |Builds clean|Deployed\.|Wait, I |Let['\u2019]s test |Good —|Great\.|Perfect\.|Alright|OK,? let me|I[''\u2019]ll |Starting |I need to |Need |I found |I read |I[''\u2019]ve (loaded|confirmed|verified)|Context loaded|Next (I[''\u2019]|step)|Deps confirm|Diff check|Still missing|I[''\u2019]ll (do|check|inspect|trace|run|grab|pull|read|verify))/;
|
|
@@ -2088,7 +2109,6 @@ init_shared();
|
|
|
2088
2109
|
import { readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
|
|
2089
2110
|
import { join as join3, basename } from "node:path";
|
|
2090
2111
|
import { homedir as homedir3, platform as platform3 } from "node:os";
|
|
2091
|
-
var MAX_CONTENT = 48000;
|
|
2092
2112
|
var MIN_TURN_LEN = 50;
|
|
2093
2113
|
function resolveProjectName(dirName) {
|
|
2094
2114
|
const segments = dirName.replace(/^-/, "").split("-");
|
|
@@ -2231,22 +2251,7 @@ function ingestClaude() {
|
|
|
2231
2251
|
continue;
|
|
2232
2252
|
}
|
|
2233
2253
|
const header = `# Chat: ${project}${date ? ` | ${date}` : ""}`;
|
|
2234
|
-
const
|
|
2235
|
-
return `## Q: ${t.user}
|
|
2236
|
-
|
|
2237
|
-
${t.assistant}`;
|
|
2238
|
-
}).join(`
|
|
2239
|
-
|
|
2240
|
-
---
|
|
2241
|
-
|
|
2242
|
-
`);
|
|
2243
|
-
let content = `${header}
|
|
2244
|
-
|
|
2245
|
-
${body}`;
|
|
2246
|
-
if (content.length > MAX_CONTENT)
|
|
2247
|
-
content = content.slice(0, MAX_CONTENT) + `
|
|
2248
|
-
|
|
2249
|
-
[truncated]`;
|
|
2254
|
+
const content = fitContent(header, turns);
|
|
2250
2255
|
const contentHash = createContentHash(content);
|
|
2251
2256
|
const dedupKey = `claude:${sessionId}`;
|
|
2252
2257
|
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
@@ -2277,7 +2282,6 @@ init_shared();
|
|
|
2277
2282
|
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3 } from "node:fs";
|
|
2278
2283
|
import { join as join4, basename as basename2 } from "node:path";
|
|
2279
2284
|
import { homedir as homedir4 } from "node:os";
|
|
2280
|
-
var MAX_CONTENT2 = 48000;
|
|
2281
2285
|
function isCodexBoilerplate(text) {
|
|
2282
2286
|
return text.startsWith("# AGENTS.md instructions for") || text.startsWith("<INSTRUCTIONS>") || text.startsWith("<user_instructions>") || text.startsWith("<user_action>");
|
|
2283
2287
|
}
|
|
@@ -2385,25 +2389,14 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
2385
2389
|
stats.filtered++;
|
|
2386
2390
|
continue;
|
|
2387
2391
|
}
|
|
2388
|
-
const
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
`
|
|
2392
|
-
return `## Q: ${r.user}
|
|
2393
|
-
|
|
2394
|
-
${assistant}`;
|
|
2395
|
-
}).join(`
|
|
2392
|
+
const header = `# Codex Session${project ? `: ${project}` : ""} | ${date || "unknown"}`;
|
|
2393
|
+
const turns = rounds.map((r) => ({
|
|
2394
|
+
user: r.user,
|
|
2395
|
+
assistant: r.assistantParts.join(`
|
|
2396
2396
|
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
let content = `# Codex Session${project ? `: ${project}` : ""} | ${date || "unknown"}
|
|
2401
|
-
|
|
2402
|
-
${body}`;
|
|
2403
|
-
if (content.length > MAX_CONTENT2)
|
|
2404
|
-
content = content.slice(0, MAX_CONTENT2) + `
|
|
2405
|
-
|
|
2406
|
-
[truncated]`;
|
|
2397
|
+
`)
|
|
2398
|
+
}));
|
|
2399
|
+
const content = fitContent(header, turns);
|
|
2407
2400
|
const contentHash = createContentHash(content);
|
|
2408
2401
|
const dedupKey = `codex:${sessionId}`;
|
|
2409
2402
|
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
@@ -2435,7 +2428,6 @@ init_shared();
|
|
|
2435
2428
|
import { readFileSync as readFileSync5, statSync } from "node:fs";
|
|
2436
2429
|
import { join as join5 } from "node:path";
|
|
2437
2430
|
import { createRequire as createRequire3 } from "node:module";
|
|
2438
|
-
var MAX_CONTENT3 = 48000;
|
|
2439
2431
|
var MAX_DB_SIZE = 2 * 1024 * 1024 * 1024;
|
|
2440
2432
|
var WARN_DB_SIZE = 500 * 1024 * 1024;
|
|
2441
2433
|
var MIN_TURN_LEN2 = 50;
|
|
@@ -2551,22 +2543,7 @@ async function ingestCursor(dbPath, wasmDir) {
|
|
|
2551
2543
|
const sessionName = parsed.name || "Cursor Chat";
|
|
2552
2544
|
const date = parsed.createdAt ? new Date(parsed.createdAt).toISOString().slice(0, 10) : "unknown";
|
|
2553
2545
|
const header = `# ${sessionName} | ${date}`;
|
|
2554
|
-
const
|
|
2555
|
-
return `## Q: ${t.user}
|
|
2556
|
-
|
|
2557
|
-
${t.assistant}`;
|
|
2558
|
-
}).join(`
|
|
2559
|
-
|
|
2560
|
-
---
|
|
2561
|
-
|
|
2562
|
-
`);
|
|
2563
|
-
let content = `${header}
|
|
2564
|
-
|
|
2565
|
-
${body}`;
|
|
2566
|
-
if (content.length > MAX_CONTENT3)
|
|
2567
|
-
content = content.slice(0, MAX_CONTENT3) + `
|
|
2568
|
-
|
|
2569
|
-
[truncated]`;
|
|
2546
|
+
const content = fitContent(header, turns);
|
|
2570
2547
|
const contentHash = createContentHash(content);
|
|
2571
2548
|
const dedupKey = `cursor:${composerId}`;
|
|
2572
2549
|
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
@@ -3115,31 +3092,6 @@ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) START sync" >> "$LOG"
|
|
|
3115
3092
|
2>> "$LOG"
|
|
3116
3093
|
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) DONE sync (exit $?)" >> "$LOG"
|
|
3117
3094
|
`;
|
|
3118
|
-
function makePlist(intervalMinutes) {
|
|
3119
|
-
const home = homedir7();
|
|
3120
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
3121
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
3122
|
-
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3123
|
-
<plist version="1.0">
|
|
3124
|
-
<dict>
|
|
3125
|
-
<key>Label</key>
|
|
3126
|
-
<string>${PLIST_LABEL}</string>
|
|
3127
|
-
<key>ProgramArguments</key>
|
|
3128
|
-
<array>
|
|
3129
|
-
<string>/bin/bash</string>
|
|
3130
|
-
<string>${home}/.conare/bin/run.sh</string>
|
|
3131
|
-
</array>
|
|
3132
|
-
<key>StartInterval</key>
|
|
3133
|
-
<integer>${intervalMinutes * 60}</integer>
|
|
3134
|
-
<key>StandardOutPath</key>
|
|
3135
|
-
<string>${home}/.conare/ingest.log</string>
|
|
3136
|
-
<key>StandardErrorPath</key>
|
|
3137
|
-
<string>${home}/.conare/ingest.log</string>
|
|
3138
|
-
<key>RunAtLoad</key>
|
|
3139
|
-
<true/>
|
|
3140
|
-
</dict>
|
|
3141
|
-
</plist>`;
|
|
3142
|
-
}
|
|
3143
3095
|
var SYSTEMD_SERVICE_CONTENT = `[Unit]
|
|
3144
3096
|
Description=Conare Memory — background sync
|
|
3145
3097
|
|
|
@@ -3241,22 +3193,12 @@ function findSqlJs() {
|
|
|
3241
3193
|
}
|
|
3242
3194
|
return null;
|
|
3243
3195
|
}
|
|
3244
|
-
function
|
|
3245
|
-
const plistDir = dirname2(PLIST_PATH);
|
|
3246
|
-
mkdirSync4(plistDir, { recursive: true });
|
|
3247
|
-
writeFileSync4(PLIST_PATH, makePlist(intervalMinutes));
|
|
3248
|
-
const id = uid();
|
|
3196
|
+
function cleanupOldLaunchAgent() {
|
|
3249
3197
|
try {
|
|
3250
|
-
execSync3(`launchctl bootout gui/${
|
|
3198
|
+
execSync3(`launchctl bootout gui/${uid()} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
3251
3199
|
} catch {}
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
} catch {
|
|
3255
|
-
try {
|
|
3256
|
-
execSync3(`launchctl load "${PLIST_PATH}"`, { stdio: "ignore" });
|
|
3257
|
-
} catch {
|
|
3258
|
-
throw new Error("Failed to load launchd agent. Try manually: launchctl load " + PLIST_PATH);
|
|
3259
|
-
}
|
|
3200
|
+
if (existsSync8(PLIST_PATH)) {
|
|
3201
|
+
unlinkSync(PLIST_PATH);
|
|
3260
3202
|
}
|
|
3261
3203
|
}
|
|
3262
3204
|
function setupLinuxSystemd(intervalMinutes) {
|
|
@@ -3266,13 +3208,30 @@ function setupLinuxSystemd(intervalMinutes) {
|
|
|
3266
3208
|
execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
3267
3209
|
execSync3("systemctl --user enable --now conare-sync.timer", { stdio: "ignore" });
|
|
3268
3210
|
}
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
const
|
|
3211
|
+
var CRON_SAFE_INTERVALS = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30];
|
|
3212
|
+
function clampCronInterval(intervalMinutes) {
|
|
3213
|
+
const n = Number.isFinite(intervalMinutes) ? Math.round(intervalMinutes) : 10;
|
|
3214
|
+
if (n <= 0)
|
|
3215
|
+
return 10;
|
|
3216
|
+
let best = 10;
|
|
3217
|
+
let bestDist = Infinity;
|
|
3218
|
+
for (const safe of CRON_SAFE_INTERVALS) {
|
|
3219
|
+
const dist = Math.abs(n - safe);
|
|
3220
|
+
if (dist < bestDist) {
|
|
3221
|
+
bestDist = dist;
|
|
3222
|
+
best = safe;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
return best;
|
|
3226
|
+
}
|
|
3227
|
+
function setupCron(intervalMinutes) {
|
|
3228
|
+
const clamped = clampCronInterval(intervalMinutes);
|
|
3229
|
+
const cronCmd = `"${homedir7()}/.conare/bin/run.sh"`;
|
|
3230
|
+
const cronLine = `*/${clamped} * * * * ${cronCmd} # conare-sync`;
|
|
3272
3231
|
try {
|
|
3273
3232
|
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
3274
3233
|
const filtered = existing.split(`
|
|
3275
|
-
`).filter((l) => !l.includes("conare")).join(`
|
|
3234
|
+
`).filter((l) => !l.endsWith("# conare-sync") && !l.includes(".conare/bin/run.sh")).join(`
|
|
3276
3235
|
`);
|
|
3277
3236
|
const newCrontab = (filtered.trim() ? filtered.trim() + `
|
|
3278
3237
|
` : "") + cronLine + `
|
|
@@ -3283,6 +3242,47 @@ function setupLinuxCron(intervalMinutes) {
|
|
|
3283
3242
|
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
3284
3243
|
}
|
|
3285
3244
|
}
|
|
3245
|
+
function removeCronEntry() {
|
|
3246
|
+
try {
|
|
3247
|
+
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
3248
|
+
const lines = existing.split(`
|
|
3249
|
+
`);
|
|
3250
|
+
const filtered = lines.filter((l) => !l.endsWith("# conare-sync") && !l.includes(".conare/bin/run.sh"));
|
|
3251
|
+
if (filtered.length === lines.length)
|
|
3252
|
+
return false;
|
|
3253
|
+
const newCrontab = filtered.join(`
|
|
3254
|
+
`).trim();
|
|
3255
|
+
if (newCrontab) {
|
|
3256
|
+
execSync3("crontab -", { input: newCrontab + `
|
|
3257
|
+
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
3258
|
+
} else {
|
|
3259
|
+
execSync3("crontab -", { input: `
|
|
3260
|
+
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
3261
|
+
}
|
|
3262
|
+
return true;
|
|
3263
|
+
} catch {
|
|
3264
|
+
return false;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
function runSyncNow() {
|
|
3268
|
+
const os = platform6();
|
|
3269
|
+
try {
|
|
3270
|
+
if (os === "win32") {
|
|
3271
|
+
const runCmd = join9(BIN_DIR, "run.cmd");
|
|
3272
|
+
if (existsSync8(runCmd)) {
|
|
3273
|
+
execSync3(`"${runCmd}"`, { stdio: "ignore", timeout: 60000 });
|
|
3274
|
+
return true;
|
|
3275
|
+
}
|
|
3276
|
+
} else {
|
|
3277
|
+
const runSh = join9(BIN_DIR, "run.sh");
|
|
3278
|
+
if (existsSync8(runSh)) {
|
|
3279
|
+
execSync3(`/bin/bash "${runSh}"`, { stdio: "ignore", timeout: 60000 });
|
|
3280
|
+
return true;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
} catch {}
|
|
3284
|
+
return false;
|
|
3285
|
+
}
|
|
3286
3286
|
function installGlobalCommand() {
|
|
3287
3287
|
const isWindows = platform6() === "win32";
|
|
3288
3288
|
if (isWindows) {
|
|
@@ -3398,13 +3398,20 @@ function persistAndInstallGlobal(apiKey) {
|
|
|
3398
3398
|
messages.push(globalMsg);
|
|
3399
3399
|
return messages;
|
|
3400
3400
|
}
|
|
3401
|
+
function sanitizeInterval(n) {
|
|
3402
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
3403
|
+
return 10;
|
|
3404
|
+
return Math.max(1, Math.round(n));
|
|
3405
|
+
}
|
|
3401
3406
|
function installSync(apiKey, intervalMinutes = 10) {
|
|
3402
3407
|
const messages = persistAndInstallGlobal(apiKey);
|
|
3403
3408
|
const os = platform6();
|
|
3409
|
+
intervalMinutes = sanitizeInterval(intervalMinutes);
|
|
3404
3410
|
if (os === "darwin") {
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3411
|
+
cleanupOldLaunchAgent();
|
|
3412
|
+
const actual = clampCronInterval(intervalMinutes);
|
|
3413
|
+
setupCron(intervalMinutes);
|
|
3414
|
+
messages.push(`Installed cron job (every ${actual} min)`);
|
|
3408
3415
|
} else if (os === "win32") {
|
|
3409
3416
|
setupWindows(intervalMinutes);
|
|
3410
3417
|
messages.push(`Installed Windows Task Scheduler (every ${intervalMinutes} min)`);
|
|
@@ -3413,25 +3420,23 @@ function installSync(apiKey, intervalMinutes = 10) {
|
|
|
3413
3420
|
setupLinuxSystemd(intervalMinutes);
|
|
3414
3421
|
messages.push(`Installed systemd timer (every ${intervalMinutes} min)`);
|
|
3415
3422
|
} else {
|
|
3416
|
-
|
|
3417
|
-
|
|
3423
|
+
const actual = clampCronInterval(intervalMinutes);
|
|
3424
|
+
setupCron(intervalMinutes);
|
|
3425
|
+
messages.push(`Installed cron job (every ${actual} min)`);
|
|
3418
3426
|
}
|
|
3419
3427
|
} else {
|
|
3420
3428
|
messages.push(`Unsupported platform: ${os}. Run manually: ~/.conare/bin/run.sh`);
|
|
3421
3429
|
}
|
|
3430
|
+
const syncOk = runSyncNow();
|
|
3431
|
+
messages.push(syncOk ? "First sync completed" : "First sync deferred to next timer tick");
|
|
3422
3432
|
return messages;
|
|
3423
3433
|
}
|
|
3424
3434
|
function uninstallSync() {
|
|
3425
3435
|
const messages = [];
|
|
3426
3436
|
const os = platform6();
|
|
3427
3437
|
if (os === "darwin") {
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
} catch {}
|
|
3431
|
-
if (existsSync8(PLIST_PATH)) {
|
|
3432
|
-
unlinkSync(PLIST_PATH);
|
|
3433
|
-
messages.push("Removed launchd agent");
|
|
3434
|
-
}
|
|
3438
|
+
removeCronEntry() && messages.push("Removed cron job");
|
|
3439
|
+
cleanupOldLaunchAgent();
|
|
3435
3440
|
} else if (os === "win32") {
|
|
3436
3441
|
try {
|
|
3437
3442
|
execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
@@ -3451,25 +3456,18 @@ function uninstallSync() {
|
|
|
3451
3456
|
} catch {}
|
|
3452
3457
|
messages.push("Removed systemd timer");
|
|
3453
3458
|
} else {
|
|
3454
|
-
|
|
3455
|
-
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
3456
|
-
const filtered = existing.split(`
|
|
3457
|
-
`).filter((l) => !l.includes("conare")).join(`
|
|
3458
|
-
`);
|
|
3459
|
-
execSync3("crontab -", { input: filtered.trim() + `
|
|
3460
|
-
`, stdio: ["pipe", "ignore", "ignore"] });
|
|
3461
|
-
messages.push("Removed cron job");
|
|
3462
|
-
} catch {}
|
|
3459
|
+
removeCronEntry() && messages.push("Removed cron job");
|
|
3463
3460
|
}
|
|
3464
3461
|
}
|
|
3465
|
-
const
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3462
|
+
const configPath = join9(CONARE_DIR, "config.json");
|
|
3463
|
+
if (existsSync8(configPath))
|
|
3464
|
+
unlinkSync(configPath);
|
|
3465
|
+
const lockDir = join9(CONARE_DIR, "sync.lock.d");
|
|
3466
|
+
if (existsSync8(lockDir))
|
|
3467
|
+
rmSync2(lockDir, { recursive: true, force: true });
|
|
3468
|
+
const lockFile = join9(CONARE_DIR, "sync.lock");
|
|
3469
|
+
if (existsSync8(lockFile))
|
|
3470
|
+
unlinkSync(lockFile);
|
|
3473
3471
|
if (existsSync8(BIN_DIR)) {
|
|
3474
3472
|
rmSync2(BIN_DIR, { recursive: true, force: true });
|
|
3475
3473
|
messages.push("Removed ~/.conare/bin/");
|