conare 0.6.0 → 0.6.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 +704 -173
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -60,18 +60,18 @@ function conareDir() {
|
|
|
60
60
|
function globalManifestPath() {
|
|
61
61
|
return join(conareDir(), "ingested.json");
|
|
62
62
|
}
|
|
63
|
-
function scopedManifestPath(
|
|
64
|
-
const slug = createHash("sha256").update(
|
|
63
|
+
function scopedManifestPath(scope) {
|
|
64
|
+
const slug = createHash("sha256").update(scope).digest("hex").slice(0, 16);
|
|
65
65
|
return join(conareDir(), `ingested.${slug}.json`);
|
|
66
66
|
}
|
|
67
67
|
function manifestPath() {
|
|
68
|
-
return
|
|
68
|
+
return activeScope ? scopedManifestPath(activeScope) : globalManifestPath();
|
|
69
69
|
}
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
if (!
|
|
70
|
+
function setManifestScope(userId, apiKey) {
|
|
71
|
+
activeScope = userId ? `${userId}:${apiKey}` : null;
|
|
72
|
+
if (!activeScope)
|
|
73
73
|
return;
|
|
74
|
-
const scoped = scopedManifestPath(
|
|
74
|
+
const scoped = scopedManifestPath(activeScope);
|
|
75
75
|
if (!existsSync(scoped) && existsSync(globalManifestPath())) {
|
|
76
76
|
try {
|
|
77
77
|
if (!existsSync(conareDir()))
|
|
@@ -240,7 +240,7 @@ function clearIngested(source) {
|
|
|
240
240
|
}
|
|
241
241
|
writeManifest(manifest);
|
|
242
242
|
}
|
|
243
|
-
var
|
|
243
|
+
var activeScope = null, MAX_MEMORY_CONTENT = 200000, TRUNCATED_USER_MSG = 3000, TRUNCATED_MARKER = `
|
|
244
244
|
|
|
245
245
|
...[truncated to fit Conare upload limit]`, MIN_SUBSTANTIVE = 200, NARRATION_RE, remoteCache, CASE_INSENSITIVE_FS;
|
|
246
246
|
var init_shared = __esm(() => {
|
|
@@ -258,16 +258,16 @@ __export(exports_codebase, {
|
|
|
258
258
|
detectProjectName: () => detectProjectName
|
|
259
259
|
});
|
|
260
260
|
import { createHash as createHash2 } from "node:crypto";
|
|
261
|
-
import { readdirSync as
|
|
262
|
-
import { join as
|
|
261
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync7, statSync as statSync2, existsSync as existsSync8 } from "node:fs";
|
|
262
|
+
import { join as join8, relative, extname, resolve, basename as basename3 } from "node:path";
|
|
263
263
|
import { execSync as execSync2 } from "node:child_process";
|
|
264
264
|
function parseGitignore(rootPath) {
|
|
265
265
|
const patterns = new Set;
|
|
266
|
-
const gitignorePath =
|
|
267
|
-
if (!
|
|
266
|
+
const gitignorePath = join8(rootPath, ".gitignore");
|
|
267
|
+
if (!existsSync8(gitignorePath))
|
|
268
268
|
return patterns;
|
|
269
269
|
try {
|
|
270
|
-
const content =
|
|
270
|
+
const content = readFileSync7(gitignorePath, "utf-8");
|
|
271
271
|
for (const line of content.split(`
|
|
272
272
|
`)) {
|
|
273
273
|
const trimmed = line.trim();
|
|
@@ -303,15 +303,15 @@ ${content}
|
|
|
303
303
|
function detectProjectName(rootPath) {
|
|
304
304
|
const readers = [
|
|
305
305
|
() => {
|
|
306
|
-
const pkg = JSON.parse(
|
|
306
|
+
const pkg = JSON.parse(readFileSync7(join8(rootPath, "package.json"), "utf-8"));
|
|
307
307
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
308
308
|
},
|
|
309
309
|
() => {
|
|
310
|
-
const content =
|
|
310
|
+
const content = readFileSync7(join8(rootPath, "Cargo.toml"), "utf-8");
|
|
311
311
|
return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
312
312
|
},
|
|
313
313
|
() => {
|
|
314
|
-
const content =
|
|
314
|
+
const content = readFileSync7(join8(rootPath, "pyproject.toml"), "utf-8");
|
|
315
315
|
return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
316
316
|
}
|
|
317
317
|
];
|
|
@@ -373,14 +373,14 @@ function indexCodebase(rootPath, options = {}) {
|
|
|
373
373
|
function walk(dir) {
|
|
374
374
|
let entries;
|
|
375
375
|
try {
|
|
376
|
-
entries =
|
|
376
|
+
entries = readdirSync7(dir, { withFileTypes: true });
|
|
377
377
|
} catch {
|
|
378
378
|
return;
|
|
379
379
|
}
|
|
380
380
|
for (const entry of entries) {
|
|
381
381
|
if (shouldIgnore(entry.name, gitignorePatterns))
|
|
382
382
|
continue;
|
|
383
|
-
const fullPath =
|
|
383
|
+
const fullPath = join8(dir, entry.name);
|
|
384
384
|
if (entry.isDirectory()) {
|
|
385
385
|
walk(fullPath);
|
|
386
386
|
continue;
|
|
@@ -408,7 +408,7 @@ function indexCodebase(rootPath, options = {}) {
|
|
|
408
408
|
}
|
|
409
409
|
let raw;
|
|
410
410
|
try {
|
|
411
|
-
raw =
|
|
411
|
+
raw = readFileSync7(fullPath, "utf-8");
|
|
412
412
|
} catch {
|
|
413
413
|
skipped++;
|
|
414
414
|
continue;
|
|
@@ -2057,13 +2057,13 @@ var init_interactive = __esm(() => {
|
|
|
2057
2057
|
});
|
|
2058
2058
|
|
|
2059
2059
|
// src/index.ts
|
|
2060
|
-
import { existsSync as
|
|
2061
|
-
import { join as
|
|
2060
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
2061
|
+
import { join as join12 } from "node:path";
|
|
2062
2062
|
|
|
2063
2063
|
// src/detect.ts
|
|
2064
|
-
import { existsSync as
|
|
2065
|
-
import { join as
|
|
2066
|
-
import { homedir as
|
|
2064
|
+
import { existsSync as existsSync7, readdirSync as readdirSync6 } from "node:fs";
|
|
2065
|
+
import { join as join7 } from "node:path";
|
|
2066
|
+
import { homedir as homedir7, platform as platform3 } from "node:os";
|
|
2067
2067
|
|
|
2068
2068
|
// src/ingest/claude.ts
|
|
2069
2069
|
init_shared();
|
|
@@ -2271,7 +2271,6 @@ async function countImportableClaudeSessions(onProgress) {
|
|
|
2271
2271
|
return count;
|
|
2272
2272
|
}
|
|
2273
2273
|
function ingestClaude(projectRoots, opts) {
|
|
2274
|
-
const dedupKeySource = opts?.dedupSource ?? "claude";
|
|
2275
2274
|
const projectsDir = join2(homedir2(), ".claude", "projects");
|
|
2276
2275
|
const memories = [];
|
|
2277
2276
|
const sessionIds = [];
|
|
@@ -2316,7 +2315,7 @@ function ingestClaude(projectRoots, opts) {
|
|
|
2316
2315
|
const contentHash = createContentHash(content);
|
|
2317
2316
|
const dedupKey = `claude:${sessionId}`;
|
|
2318
2317
|
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
2319
|
-
if (!opts?.includeIngested && isIngested(
|
|
2318
|
+
if (!opts?.includeIngested && isIngested("claude", fingerprint)) {
|
|
2320
2319
|
deduped++;
|
|
2321
2320
|
continue;
|
|
2322
2321
|
}
|
|
@@ -2365,7 +2364,7 @@ function projectFromCwd(cwd) {
|
|
|
2365
2364
|
}
|
|
2366
2365
|
return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
|
|
2367
2366
|
}
|
|
2368
|
-
function ingestCodex(projectRoots) {
|
|
2367
|
+
function ingestCodex(projectRoots, opts) {
|
|
2369
2368
|
const memories = [];
|
|
2370
2369
|
const sessionIds = [];
|
|
2371
2370
|
let filtered = 0;
|
|
@@ -2374,7 +2373,7 @@ function ingestCodex(projectRoots) {
|
|
|
2374
2373
|
if (existsSync3(sessionsDir)) {
|
|
2375
2374
|
try {
|
|
2376
2375
|
const stats = { filtered: 0, deduped: 0 };
|
|
2377
|
-
walkCodexSessions(sessionsDir, memories, sessionIds, stats, projectRoots);
|
|
2376
|
+
walkCodexSessions(sessionsDir, memories, sessionIds, stats, projectRoots, opts?.includeIngested);
|
|
2378
2377
|
filtered += stats.filtered;
|
|
2379
2378
|
deduped += stats.deduped;
|
|
2380
2379
|
} catch {}
|
|
@@ -2540,6 +2539,7 @@ function walkCodexSessions(dir, memories, sessionIds, stats, projectRoots, inclu
|
|
|
2540
2539
|
stats.deduped++;
|
|
2541
2540
|
continue;
|
|
2542
2541
|
}
|
|
2542
|
+
const sourceRemote = cwd ? repoRemote(cwd) : undefined;
|
|
2543
2543
|
memories.push({
|
|
2544
2544
|
content,
|
|
2545
2545
|
containerTag: "codex-chats",
|
|
@@ -2549,6 +2549,7 @@ function walkCodexSessions(dir, memories, sessionIds, stats, projectRoots, inclu
|
|
|
2549
2549
|
source: "codex-session",
|
|
2550
2550
|
sessionId,
|
|
2551
2551
|
...cwd ? { sourceRoot: cwd } : {},
|
|
2552
|
+
...sourceRemote ? { sourceRemote } : {},
|
|
2552
2553
|
date: date || "unknown",
|
|
2553
2554
|
...sourceTimestamp ? { sourceTimestamp } : {},
|
|
2554
2555
|
...startedAt ? { sessionStartedAt: startedAt } : {},
|
|
@@ -3032,7 +3033,7 @@ async function ingestCursor(dbPath, wasmDir, projectRoots, opts) {
|
|
|
3032
3033
|
const contentHash = createContentHash(content);
|
|
3033
3034
|
const dedupKey = `cursor:${composerId}`;
|
|
3034
3035
|
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
3035
|
-
if (!opts?.includeIngested && isIngested(
|
|
3036
|
+
if (!opts?.includeIngested && isIngested("cursor", fingerprint)) {
|
|
3036
3037
|
deduped++;
|
|
3037
3038
|
continue;
|
|
3038
3039
|
}
|
|
@@ -3066,14 +3067,410 @@ async function ingestCursor(dbPath, wasmDir, projectRoots, opts) {
|
|
|
3066
3067
|
return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
|
|
3067
3068
|
}
|
|
3068
3069
|
|
|
3070
|
+
// src/ingest/opencode.ts
|
|
3071
|
+
init_shared();
|
|
3072
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
3073
|
+
import { join as join5 } from "node:path";
|
|
3074
|
+
import { homedir as homedir5 } from "node:os";
|
|
3075
|
+
function dataDirs() {
|
|
3076
|
+
const override = process.env.OPENCODE_DATA_DIR;
|
|
3077
|
+
if (override)
|
|
3078
|
+
return override.split(",").map((d) => d.trim()).filter(Boolean);
|
|
3079
|
+
return [join5(homedir5(), ".local", "share", "opencode")];
|
|
3080
|
+
}
|
|
3081
|
+
function storageDirs() {
|
|
3082
|
+
return dataDirs().map((d) => join5(d, "storage")).filter(existsSync5);
|
|
3083
|
+
}
|
|
3084
|
+
function readSessions(storage) {
|
|
3085
|
+
const out = [];
|
|
3086
|
+
const sessionRoot = join5(storage, "session");
|
|
3087
|
+
if (!existsSync5(sessionRoot))
|
|
3088
|
+
return out;
|
|
3089
|
+
const walk = (dir) => {
|
|
3090
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
3091
|
+
const full = join5(dir, entry.name);
|
|
3092
|
+
if (entry.isDirectory()) {
|
|
3093
|
+
walk(full);
|
|
3094
|
+
continue;
|
|
3095
|
+
}
|
|
3096
|
+
if (!entry.name.endsWith(".json"))
|
|
3097
|
+
continue;
|
|
3098
|
+
try {
|
|
3099
|
+
const s = JSON.parse(readFileSync5(full, "utf-8"));
|
|
3100
|
+
if (typeof s.id !== "string")
|
|
3101
|
+
continue;
|
|
3102
|
+
out.push({
|
|
3103
|
+
id: s.id,
|
|
3104
|
+
directory: typeof s.directory === "string" ? s.directory : null,
|
|
3105
|
+
createdMs: typeof s.time?.created === "number" ? s.time.created : null,
|
|
3106
|
+
updatedMs: typeof s.time?.updated === "number" ? s.time.updated : null
|
|
3107
|
+
});
|
|
3108
|
+
} catch {}
|
|
3109
|
+
}
|
|
3110
|
+
};
|
|
3111
|
+
try {
|
|
3112
|
+
walk(sessionRoot);
|
|
3113
|
+
} catch {}
|
|
3114
|
+
return out;
|
|
3115
|
+
}
|
|
3116
|
+
function messageText(storage, messageId) {
|
|
3117
|
+
const partDir = join5(storage, "part", messageId);
|
|
3118
|
+
if (!existsSync5(partDir))
|
|
3119
|
+
return "";
|
|
3120
|
+
const texts = [];
|
|
3121
|
+
let files;
|
|
3122
|
+
try {
|
|
3123
|
+
files = readdirSync4(partDir).filter((f) => f.endsWith(".json")).sort();
|
|
3124
|
+
} catch {
|
|
3125
|
+
return "";
|
|
3126
|
+
}
|
|
3127
|
+
for (const f of files) {
|
|
3128
|
+
try {
|
|
3129
|
+
const p = JSON.parse(readFileSync5(join5(partDir, f), "utf-8"));
|
|
3130
|
+
if (p.type === "text" && typeof p.text === "string")
|
|
3131
|
+
texts.push(p.text);
|
|
3132
|
+
} catch {}
|
|
3133
|
+
}
|
|
3134
|
+
return texts.join(`
|
|
3135
|
+
|
|
3136
|
+
`);
|
|
3137
|
+
}
|
|
3138
|
+
function buildRounds(storage, sessionId) {
|
|
3139
|
+
const msgDir = join5(storage, "message", sessionId);
|
|
3140
|
+
if (!existsSync5(msgDir))
|
|
3141
|
+
return [];
|
|
3142
|
+
let entries = [];
|
|
3143
|
+
try {
|
|
3144
|
+
for (const f of readdirSync4(msgDir)) {
|
|
3145
|
+
if (!f.endsWith(".json"))
|
|
3146
|
+
continue;
|
|
3147
|
+
const m = JSON.parse(readFileSync5(join5(msgDir, f), "utf-8"));
|
|
3148
|
+
if (typeof m.id !== "string" || typeof m.role !== "string")
|
|
3149
|
+
continue;
|
|
3150
|
+
entries.push({ id: m.id, role: m.role, createdMs: typeof m.time?.created === "number" ? m.time.created : 0 });
|
|
3151
|
+
}
|
|
3152
|
+
} catch {
|
|
3153
|
+
return [];
|
|
3154
|
+
}
|
|
3155
|
+
entries.sort((a, b) => a.createdMs - b.createdMs);
|
|
3156
|
+
const rounds = [];
|
|
3157
|
+
let currentUser = null;
|
|
3158
|
+
let currentAssistant = [];
|
|
3159
|
+
for (const e of entries) {
|
|
3160
|
+
const text = cleanText(messageText(storage, e.id));
|
|
3161
|
+
if (!text)
|
|
3162
|
+
continue;
|
|
3163
|
+
if (e.role === "user") {
|
|
3164
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
3165
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
3166
|
+
}
|
|
3167
|
+
currentUser = text;
|
|
3168
|
+
currentAssistant = [];
|
|
3169
|
+
} else if (e.role === "assistant") {
|
|
3170
|
+
if (!isNarration(text))
|
|
3171
|
+
currentAssistant.push(text);
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
3175
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
3176
|
+
}
|
|
3177
|
+
return rounds;
|
|
3178
|
+
}
|
|
3179
|
+
function ingestOpenCode(projectRoots, opts) {
|
|
3180
|
+
const memories = [];
|
|
3181
|
+
const sessionIds = [];
|
|
3182
|
+
let filtered = 0;
|
|
3183
|
+
let deduped = 0;
|
|
3184
|
+
for (const storage of storageDirs()) {
|
|
3185
|
+
for (const session of readSessions(storage)) {
|
|
3186
|
+
const cwd = session.directory;
|
|
3187
|
+
if (!matchesProjectFilter(cwd, projectRoots)) {
|
|
3188
|
+
filtered++;
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
const rounds = buildRounds(storage, session.id);
|
|
3192
|
+
if (rounds.length === 0) {
|
|
3193
|
+
filtered++;
|
|
3194
|
+
continue;
|
|
3195
|
+
}
|
|
3196
|
+
const date = session.createdMs ? new Date(session.createdMs).toISOString().slice(0, 10) : null;
|
|
3197
|
+
const project = cwd ? projectFromCwd2(cwd) : null;
|
|
3198
|
+
const header = `# OpenCode Session${project ? `: ${project}` : ""} | ${date || "unknown"}`;
|
|
3199
|
+
const turns = rounds.map((r) => ({ user: r.user, assistant: r.assistantParts.join(`
|
|
3200
|
+
|
|
3201
|
+
`) }));
|
|
3202
|
+
const content = fitContent(header, turns);
|
|
3203
|
+
const contentHash = createContentHash(content);
|
|
3204
|
+
const dedupKey = `opencode:${session.id}`;
|
|
3205
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
3206
|
+
if (!opts?.includeIngested && isIngested("opencode", fingerprint)) {
|
|
3207
|
+
deduped++;
|
|
3208
|
+
continue;
|
|
3209
|
+
}
|
|
3210
|
+
const sourceRemote = cwd ? repoRemote(cwd) : undefined;
|
|
3211
|
+
const sourceTimestamp = session.updatedMs ?? session.createdMs ?? null;
|
|
3212
|
+
memories.push({
|
|
3213
|
+
content,
|
|
3214
|
+
containerTag: "opencode-chats",
|
|
3215
|
+
metadata: {
|
|
3216
|
+
dedupKey,
|
|
3217
|
+
contentHash,
|
|
3218
|
+
source: "opencode-session",
|
|
3219
|
+
sessionId: session.id,
|
|
3220
|
+
...cwd ? { sourceRoot: cwd } : {},
|
|
3221
|
+
...sourceRemote ? { sourceRemote } : {},
|
|
3222
|
+
date: date || "unknown",
|
|
3223
|
+
...sourceTimestamp ? { sourceTimestamp } : {},
|
|
3224
|
+
...session.createdMs ? { sessionStartedAt: new Date(session.createdMs).toISOString() } : {},
|
|
3225
|
+
...session.updatedMs ? { sessionUpdatedAt: new Date(session.updatedMs).toISOString() } : {},
|
|
3226
|
+
...project ? { project } : {}
|
|
3227
|
+
},
|
|
3228
|
+
...sourceTimestamp ? { created_at: sourceTimestamp, updated_at: sourceTimestamp } : {}
|
|
3229
|
+
});
|
|
3230
|
+
sessionIds.push(session.id);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
|
|
3234
|
+
}
|
|
3235
|
+
function projectFromCwd2(cwd) {
|
|
3236
|
+
const home = homedir5().replace(/\\/g, "/");
|
|
3237
|
+
const normalized = cwd.replace(/\\/g, "/");
|
|
3238
|
+
if (normalized.startsWith(home + "/"))
|
|
3239
|
+
return normalized.slice(home.length + 1);
|
|
3240
|
+
return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
|
|
3241
|
+
}
|
|
3242
|
+
async function countImportableOpenCodeSessions(onProgress) {
|
|
3243
|
+
const storages = storageDirs();
|
|
3244
|
+
if (storages.length === 0)
|
|
3245
|
+
return 0;
|
|
3246
|
+
let found = 0;
|
|
3247
|
+
let checked = 0;
|
|
3248
|
+
let total = 0;
|
|
3249
|
+
const work = [];
|
|
3250
|
+
for (const storage of storages) {
|
|
3251
|
+
for (const s of readSessions(storage))
|
|
3252
|
+
work.push({ storage, sessionId: s.id });
|
|
3253
|
+
}
|
|
3254
|
+
total = work.length;
|
|
3255
|
+
for (const { storage, sessionId } of work) {
|
|
3256
|
+
if (buildRounds(storage, sessionId).length > 0)
|
|
3257
|
+
found++;
|
|
3258
|
+
checked++;
|
|
3259
|
+
if (checked % 50 === 0)
|
|
3260
|
+
onProgress?.({ checked, found, total });
|
|
3261
|
+
}
|
|
3262
|
+
onProgress?.({ checked, found, total });
|
|
3263
|
+
return found;
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
// src/ingest/grok.ts
|
|
3267
|
+
init_shared();
|
|
3268
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync5 } from "node:fs";
|
|
3269
|
+
import { join as join6 } from "node:path";
|
|
3270
|
+
import { homedir as homedir6 } from "node:os";
|
|
3271
|
+
var MIN_TURN_LEN3 = 50;
|
|
3272
|
+
function dataDirs2() {
|
|
3273
|
+
const override = process.env.GROK_DATA_DIR;
|
|
3274
|
+
if (override)
|
|
3275
|
+
return override.split(",").map((d) => d.trim()).filter(Boolean);
|
|
3276
|
+
return [join6(homedir6(), ".grok")];
|
|
3277
|
+
}
|
|
3278
|
+
function sessionRoots() {
|
|
3279
|
+
return dataDirs2().map((d) => join6(d, "sessions")).filter(existsSync6);
|
|
3280
|
+
}
|
|
3281
|
+
function readSessions2(sessionsRoot) {
|
|
3282
|
+
const out = [];
|
|
3283
|
+
let cwdDirs;
|
|
3284
|
+
try {
|
|
3285
|
+
cwdDirs = readdirSync5(sessionsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3286
|
+
} catch {
|
|
3287
|
+
return out;
|
|
3288
|
+
}
|
|
3289
|
+
for (const cwdDir of cwdDirs) {
|
|
3290
|
+
const cwdPath = join6(sessionsRoot, cwdDir);
|
|
3291
|
+
let sessionDirs;
|
|
3292
|
+
try {
|
|
3293
|
+
sessionDirs = readdirSync5(cwdPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3294
|
+
} catch {
|
|
3295
|
+
continue;
|
|
3296
|
+
}
|
|
3297
|
+
for (const sid of sessionDirs) {
|
|
3298
|
+
const dir = join6(cwdPath, sid);
|
|
3299
|
+
if (!existsSync6(join6(dir, "chat_history.jsonl")))
|
|
3300
|
+
continue;
|
|
3301
|
+
let summary = {};
|
|
3302
|
+
try {
|
|
3303
|
+
summary = JSON.parse(readFileSync6(join6(dir, "summary.json"), "utf-8"));
|
|
3304
|
+
} catch {}
|
|
3305
|
+
const cwd = typeof summary.info?.cwd === "string" ? summary.info.cwd : safeDecode(cwdDir);
|
|
3306
|
+
const remotes = Array.isArray(summary.git_remotes) ? summary.git_remotes.filter((r) => typeof r === "string") : [];
|
|
3307
|
+
out.push({
|
|
3308
|
+
id: sid,
|
|
3309
|
+
dir,
|
|
3310
|
+
cwd,
|
|
3311
|
+
gitRoot: typeof summary.git_root_dir === "string" ? summary.git_root_dir : null,
|
|
3312
|
+
gitRemote: remotes[0] ?? null,
|
|
3313
|
+
title: typeof summary.generated_title === "string" ? summary.generated_title : typeof summary.session_summary === "string" ? summary.session_summary : null,
|
|
3314
|
+
createdAt: typeof summary.created_at === "string" ? summary.created_at : null,
|
|
3315
|
+
updatedAt: typeof summary.updated_at === "string" ? summary.updated_at : null
|
|
3316
|
+
});
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
return out;
|
|
3320
|
+
}
|
|
3321
|
+
function safeDecode(name) {
|
|
3322
|
+
try {
|
|
3323
|
+
return decodeURIComponent(name);
|
|
3324
|
+
} catch {
|
|
3325
|
+
return name || null;
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
function extractText2(content) {
|
|
3329
|
+
if (typeof content === "string")
|
|
3330
|
+
return content;
|
|
3331
|
+
if (!Array.isArray(content))
|
|
3332
|
+
return "";
|
|
3333
|
+
return content.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
|
|
3334
|
+
`);
|
|
3335
|
+
}
|
|
3336
|
+
function userQuery(text) {
|
|
3337
|
+
const m = text.match(/<user_query>([\s\S]*?)<\/user_query>/);
|
|
3338
|
+
return m ? cleanText(m[1]) : "";
|
|
3339
|
+
}
|
|
3340
|
+
function parseRounds(lines) {
|
|
3341
|
+
const rounds = [];
|
|
3342
|
+
let currentUser = null;
|
|
3343
|
+
let currentAssistant = [];
|
|
3344
|
+
for (const line of lines) {
|
|
3345
|
+
if (!line.trim())
|
|
3346
|
+
continue;
|
|
3347
|
+
let obj;
|
|
3348
|
+
try {
|
|
3349
|
+
obj = JSON.parse(line);
|
|
3350
|
+
} catch {
|
|
3351
|
+
continue;
|
|
3352
|
+
}
|
|
3353
|
+
if (obj.type === "user") {
|
|
3354
|
+
const query = userQuery(extractText2(obj.content));
|
|
3355
|
+
if (query.length >= MIN_TURN_LEN3) {
|
|
3356
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
3357
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
3358
|
+
}
|
|
3359
|
+
currentUser = query;
|
|
3360
|
+
currentAssistant = [];
|
|
3361
|
+
}
|
|
3362
|
+
} else if (obj.type === "assistant") {
|
|
3363
|
+
const text = cleanText(extractText2(obj.content));
|
|
3364
|
+
if (text.length >= MIN_TURN_LEN3 && !isNarration(text))
|
|
3365
|
+
currentAssistant.push(text);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
3369
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
3370
|
+
}
|
|
3371
|
+
return rounds.map((r) => ({ user: r.user, assistant: r.assistantParts.join(`
|
|
3372
|
+
|
|
3373
|
+
`) })).filter((t) => t.assistant.length >= MIN_TURN_LEN3);
|
|
3374
|
+
}
|
|
3375
|
+
function readRounds(sessionDir) {
|
|
3376
|
+
let raw;
|
|
3377
|
+
try {
|
|
3378
|
+
raw = readFileSync6(join6(sessionDir, "chat_history.jsonl"), "utf-8");
|
|
3379
|
+
} catch {
|
|
3380
|
+
return [];
|
|
3381
|
+
}
|
|
3382
|
+
return parseRounds(raw.split(`
|
|
3383
|
+
`));
|
|
3384
|
+
}
|
|
3385
|
+
function ingestGrok(projectRoots, opts) {
|
|
3386
|
+
const memories = [];
|
|
3387
|
+
const sessionIds = [];
|
|
3388
|
+
let filtered = 0;
|
|
3389
|
+
let deduped = 0;
|
|
3390
|
+
for (const root of sessionRoots()) {
|
|
3391
|
+
for (const session of readSessions2(root)) {
|
|
3392
|
+
const cwd = session.gitRoot ?? session.cwd;
|
|
3393
|
+
if (!matchesProjectFilter(cwd, projectRoots)) {
|
|
3394
|
+
filtered++;
|
|
3395
|
+
continue;
|
|
3396
|
+
}
|
|
3397
|
+
const turns = readRounds(session.dir);
|
|
3398
|
+
if (turns.length === 0) {
|
|
3399
|
+
filtered++;
|
|
3400
|
+
continue;
|
|
3401
|
+
}
|
|
3402
|
+
const project = cwd ? projectFromCwd3(cwd) : null;
|
|
3403
|
+
const date = session.createdAt ? session.createdAt.slice(0, 10) : null;
|
|
3404
|
+
const header = `# Grok Session${project ? `: ${project}` : ""}${session.title ? ` — ${session.title}` : ""} | ${date || "unknown"}`;
|
|
3405
|
+
const content = fitContent(header, turns);
|
|
3406
|
+
const contentHash = createContentHash(content);
|
|
3407
|
+
const dedupKey = `grok:${session.id}`;
|
|
3408
|
+
const fingerprint = `${dedupKey}:${contentHash}`;
|
|
3409
|
+
if (!opts?.includeIngested && isIngested("grok", fingerprint)) {
|
|
3410
|
+
deduped++;
|
|
3411
|
+
continue;
|
|
3412
|
+
}
|
|
3413
|
+
const sourceRemote = session.gitRemote ?? (cwd ? repoRemote(cwd) : undefined);
|
|
3414
|
+
const sourceTimestamp = parseTimestampMs(session.updatedAt ?? session.createdAt ?? "");
|
|
3415
|
+
memories.push({
|
|
3416
|
+
content,
|
|
3417
|
+
containerTag: "grok-chats",
|
|
3418
|
+
metadata: {
|
|
3419
|
+
dedupKey,
|
|
3420
|
+
contentHash,
|
|
3421
|
+
source: "grok-session",
|
|
3422
|
+
sessionId: session.id,
|
|
3423
|
+
...cwd ? { sourceRoot: cwd } : {},
|
|
3424
|
+
...sourceRemote ? { sourceRemote } : {},
|
|
3425
|
+
date: date || "unknown",
|
|
3426
|
+
...sourceTimestamp ? { sourceTimestamp } : {},
|
|
3427
|
+
...session.createdAt ? { sessionStartedAt: session.createdAt } : {},
|
|
3428
|
+
...session.updatedAt ? { sessionUpdatedAt: session.updatedAt } : {},
|
|
3429
|
+
...project ? { project } : {}
|
|
3430
|
+
},
|
|
3431
|
+
...sourceTimestamp ? { created_at: sourceTimestamp, updated_at: sourceTimestamp } : {}
|
|
3432
|
+
});
|
|
3433
|
+
sessionIds.push(session.id);
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
|
|
3437
|
+
}
|
|
3438
|
+
function projectFromCwd3(cwd) {
|
|
3439
|
+
const home = homedir6().replace(/\\/g, "/");
|
|
3440
|
+
const normalized = cwd.replace(/\\/g, "/").replace(/\/$/, "");
|
|
3441
|
+
if (normalized.startsWith(home + "/"))
|
|
3442
|
+
return normalized.slice(home.length + 1);
|
|
3443
|
+
return normalized.replace(/^\/Users\/[^/]+\//, "").replace(/^\/home\/[^/]+\//, "").replace(/^[A-Za-z]:\/Users\/[^/]+\//, "");
|
|
3444
|
+
}
|
|
3445
|
+
async function countImportableGrokSessions(onProgress) {
|
|
3446
|
+
const roots = sessionRoots();
|
|
3447
|
+
if (roots.length === 0)
|
|
3448
|
+
return 0;
|
|
3449
|
+
const sessions = [];
|
|
3450
|
+
for (const root of roots)
|
|
3451
|
+
sessions.push(...readSessions2(root));
|
|
3452
|
+
let found = 0;
|
|
3453
|
+
let checked = 0;
|
|
3454
|
+
const total = sessions.length;
|
|
3455
|
+
for (const session of sessions) {
|
|
3456
|
+
if (readRounds(session.dir).length > 0)
|
|
3457
|
+
found++;
|
|
3458
|
+
checked++;
|
|
3459
|
+
if (checked % 25 === 0)
|
|
3460
|
+
onProgress?.({ checked, found, total });
|
|
3461
|
+
}
|
|
3462
|
+
onProgress?.({ checked, found, total });
|
|
3463
|
+
return found;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3069
3466
|
// src/detect.ts
|
|
3070
3467
|
async function detect(options = {}) {
|
|
3071
|
-
const home =
|
|
3468
|
+
const home = homedir7();
|
|
3072
3469
|
const os = platform3();
|
|
3073
3470
|
const tools = [];
|
|
3074
3471
|
const onProgress = options.onProgress;
|
|
3075
|
-
const claudeDir =
|
|
3076
|
-
if (
|
|
3472
|
+
const claudeDir = join7(home, ".claude", "projects");
|
|
3473
|
+
if (existsSync7(claudeDir)) {
|
|
3077
3474
|
onProgress?.({ tool: "Claude Code", status: "start" });
|
|
3078
3475
|
const sessionCount = await countImportableClaudeSessions((progress) => {
|
|
3079
3476
|
onProgress?.({ tool: "Claude Code", status: "progress", ...progress });
|
|
@@ -3084,9 +3481,9 @@ async function detect(options = {}) {
|
|
|
3084
3481
|
onProgress?.({ tool: "Claude Code", status: "skip", reason: "not found" });
|
|
3085
3482
|
tools.push({ name: "Claude Code", id: "claude", available: false, path: claudeDir, sessionCount: 0 });
|
|
3086
3483
|
}
|
|
3087
|
-
const codexConfig =
|
|
3088
|
-
const codexSessions =
|
|
3089
|
-
if (
|
|
3484
|
+
const codexConfig = join7(home, ".codex", "config.toml");
|
|
3485
|
+
const codexSessions = join7(home, ".codex", "sessions");
|
|
3486
|
+
if (existsSync7(codexConfig) || existsSync7(codexSessions)) {
|
|
3090
3487
|
onProgress?.({ tool: "Codex", status: "start" });
|
|
3091
3488
|
const sessionCount = await countImportableCodexSessions((progress) => {
|
|
3092
3489
|
onProgress?.({ tool: "Codex", status: "progress", ...progress });
|
|
@@ -3099,13 +3496,13 @@ async function detect(options = {}) {
|
|
|
3099
3496
|
}
|
|
3100
3497
|
let cursorDbPath;
|
|
3101
3498
|
if (os === "darwin") {
|
|
3102
|
-
cursorDbPath =
|
|
3499
|
+
cursorDbPath = join7(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
3103
3500
|
} else if (os === "win32") {
|
|
3104
|
-
cursorDbPath =
|
|
3501
|
+
cursorDbPath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
3105
3502
|
} else {
|
|
3106
|
-
cursorDbPath =
|
|
3503
|
+
cursorDbPath = join7(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
3107
3504
|
}
|
|
3108
|
-
const cursorExists =
|
|
3505
|
+
const cursorExists = existsSync7(cursorDbPath);
|
|
3109
3506
|
if (cursorExists)
|
|
3110
3507
|
onProgress?.({ tool: "Cursor", status: "start" });
|
|
3111
3508
|
else
|
|
@@ -3123,73 +3520,111 @@ async function detect(options = {}) {
|
|
|
3123
3520
|
sessionCount: cursorCount,
|
|
3124
3521
|
sessionCountApproximate: true
|
|
3125
3522
|
});
|
|
3126
|
-
const
|
|
3523
|
+
const opencodeStorage = join7(process.env.OPENCODE_DATA_DIR?.split(",")[0]?.trim() || join7(home, ".local", "share", "opencode"), "storage");
|
|
3524
|
+
const opencodeExists = existsSync7(opencodeStorage);
|
|
3525
|
+
if (opencodeExists)
|
|
3526
|
+
onProgress?.({ tool: "OpenCode", status: "start" });
|
|
3527
|
+
else
|
|
3528
|
+
onProgress?.({ tool: "OpenCode", status: "skip", reason: "not found" });
|
|
3529
|
+
const opencodeCount = opencodeExists ? await countImportableOpenCodeSessions((progress) => {
|
|
3530
|
+
onProgress?.({ tool: "OpenCode", status: "progress", ...progress });
|
|
3531
|
+
}) : 0;
|
|
3532
|
+
if (opencodeExists)
|
|
3533
|
+
onProgress?.({ tool: "OpenCode", status: "done", count: opencodeCount });
|
|
3534
|
+
tools.push({
|
|
3535
|
+
name: "OpenCode",
|
|
3536
|
+
id: "opencode",
|
|
3537
|
+
available: opencodeExists,
|
|
3538
|
+
path: join7(home, ".config", "opencode", "opencode.json"),
|
|
3539
|
+
sessionCount: opencodeCount,
|
|
3540
|
+
sessionCountApproximate: true
|
|
3541
|
+
});
|
|
3542
|
+
const grokSessions = join7(process.env.GROK_DATA_DIR?.split(",")[0]?.trim() || join7(home, ".grok"), "sessions");
|
|
3543
|
+
const grokExists = existsSync7(grokSessions);
|
|
3544
|
+
if (grokExists)
|
|
3545
|
+
onProgress?.({ tool: "Grok", status: "start" });
|
|
3546
|
+
else
|
|
3547
|
+
onProgress?.({ tool: "Grok", status: "skip", reason: "not found" });
|
|
3548
|
+
const grokCount = grokExists ? await countImportableGrokSessions((progress) => {
|
|
3549
|
+
onProgress?.({ tool: "Grok", status: "progress", ...progress });
|
|
3550
|
+
}) : 0;
|
|
3551
|
+
if (grokExists)
|
|
3552
|
+
onProgress?.({ tool: "Grok", status: "done", count: grokCount });
|
|
3553
|
+
tools.push({
|
|
3554
|
+
name: "Grok",
|
|
3555
|
+
id: "grok",
|
|
3556
|
+
available: grokExists,
|
|
3557
|
+
path: join7(home, ".grok", "config.toml"),
|
|
3558
|
+
sessionCount: grokCount,
|
|
3559
|
+
sessionCountApproximate: true
|
|
3560
|
+
});
|
|
3561
|
+
const windsurfDir = join7(home, ".codeium", "windsurf");
|
|
3127
3562
|
tools.push({
|
|
3128
3563
|
name: "Windsurf",
|
|
3129
3564
|
id: "windsurf",
|
|
3130
|
-
available:
|
|
3131
|
-
path:
|
|
3565
|
+
available: existsSync7(windsurfDir),
|
|
3566
|
+
path: join7(windsurfDir, "mcp_config.json"),
|
|
3132
3567
|
sessionCount: 0
|
|
3133
3568
|
});
|
|
3134
3569
|
let vscodePath;
|
|
3135
3570
|
if (os === "darwin") {
|
|
3136
|
-
vscodePath =
|
|
3571
|
+
vscodePath = join7(home, "Library", "Application Support", "Code");
|
|
3137
3572
|
} else if (os === "win32") {
|
|
3138
|
-
vscodePath =
|
|
3573
|
+
vscodePath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Code");
|
|
3139
3574
|
} else {
|
|
3140
|
-
vscodePath =
|
|
3575
|
+
vscodePath = join7(home, ".config", "Code");
|
|
3141
3576
|
}
|
|
3142
3577
|
tools.push({
|
|
3143
3578
|
name: "VS Code Copilot",
|
|
3144
3579
|
id: "vscode",
|
|
3145
|
-
available:
|
|
3146
|
-
path:
|
|
3580
|
+
available: existsSync7(vscodePath),
|
|
3581
|
+
path: join7(vscodePath, "User", "mcp.json"),
|
|
3147
3582
|
sessionCount: 0
|
|
3148
3583
|
});
|
|
3149
3584
|
let clinePath;
|
|
3150
3585
|
if (os === "darwin") {
|
|
3151
|
-
clinePath =
|
|
3586
|
+
clinePath = join7(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
3152
3587
|
} else if (os === "win32") {
|
|
3153
|
-
clinePath =
|
|
3588
|
+
clinePath = join7(process.env.APPDATA || join7(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
3154
3589
|
} else {
|
|
3155
|
-
clinePath =
|
|
3590
|
+
clinePath = join7(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
3156
3591
|
}
|
|
3157
3592
|
tools.push({
|
|
3158
3593
|
name: "Cline",
|
|
3159
3594
|
id: "cline",
|
|
3160
|
-
available:
|
|
3161
|
-
path:
|
|
3595
|
+
available: existsSync7(clinePath),
|
|
3596
|
+
path: join7(clinePath, "settings", "cline_mcp_settings.json"),
|
|
3162
3597
|
sessionCount: 0
|
|
3163
3598
|
});
|
|
3164
|
-
const zedPath = os === "darwin" ?
|
|
3599
|
+
const zedPath = os === "darwin" ? join7(home, ".zed") : join7(home, ".config", "zed");
|
|
3165
3600
|
tools.push({
|
|
3166
3601
|
name: "Zed",
|
|
3167
3602
|
id: "zed",
|
|
3168
|
-
available:
|
|
3169
|
-
path:
|
|
3603
|
+
available: existsSync7(zedPath),
|
|
3604
|
+
path: join7(zedPath, "settings.json"),
|
|
3170
3605
|
sessionCount: 0
|
|
3171
3606
|
});
|
|
3172
|
-
const openclawDir =
|
|
3607
|
+
const openclawDir = join7(home, ".openclaw");
|
|
3173
3608
|
tools.push({
|
|
3174
3609
|
name: "OpenClaw",
|
|
3175
3610
|
id: "openclaw",
|
|
3176
|
-
available:
|
|
3177
|
-
path:
|
|
3611
|
+
available: existsSync7(openclawDir),
|
|
3612
|
+
path: join7(openclawDir, "openclaw.json"),
|
|
3178
3613
|
sessionCount: 0
|
|
3179
3614
|
});
|
|
3180
|
-
const antigravityDir =
|
|
3181
|
-
const antigravityConvDir =
|
|
3615
|
+
const antigravityDir = join7(home, ".gemini", "antigravity");
|
|
3616
|
+
const antigravityConvDir = join7(antigravityDir, "conversations");
|
|
3182
3617
|
let antigravityCount = 0;
|
|
3183
|
-
if (
|
|
3618
|
+
if (existsSync7(antigravityConvDir)) {
|
|
3184
3619
|
try {
|
|
3185
|
-
antigravityCount =
|
|
3620
|
+
antigravityCount = readdirSync6(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
|
|
3186
3621
|
} catch {}
|
|
3187
3622
|
}
|
|
3188
3623
|
tools.push({
|
|
3189
3624
|
name: "Antigravity",
|
|
3190
3625
|
id: "antigravity",
|
|
3191
|
-
available:
|
|
3192
|
-
path:
|
|
3626
|
+
available: existsSync7(antigravityDir),
|
|
3627
|
+
path: join7(antigravityDir, "mcp_config.json"),
|
|
3193
3628
|
sessionCount: antigravityCount
|
|
3194
3629
|
});
|
|
3195
3630
|
return tools;
|
|
@@ -3205,7 +3640,10 @@ async function browserAuth() {
|
|
|
3205
3640
|
const state = Array.from(stateBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
3206
3641
|
const sessionRes = await fetch(`${API_URL}/api/auth/cli-session`, {
|
|
3207
3642
|
method: "POST",
|
|
3208
|
-
headers: {
|
|
3643
|
+
headers: {
|
|
3644
|
+
"Content-Type": "application/json",
|
|
3645
|
+
"x-conare-platform": `${platform4()} node/${process.versions.node}`
|
|
3646
|
+
},
|
|
3209
3647
|
body: JSON.stringify({ state })
|
|
3210
3648
|
});
|
|
3211
3649
|
if (!sessionRes.ok) {
|
|
@@ -3280,9 +3718,9 @@ init_shared();
|
|
|
3280
3718
|
init_api();
|
|
3281
3719
|
|
|
3282
3720
|
// src/configure.ts
|
|
3283
|
-
import { existsSync as
|
|
3284
|
-
import { dirname as dirname2, join as
|
|
3285
|
-
import { homedir as
|
|
3721
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
|
|
3722
|
+
import { dirname as dirname2, join as join9 } from "node:path";
|
|
3723
|
+
import { homedir as homedir8, platform as platform5 } from "node:os";
|
|
3286
3724
|
import { spawnSync } from "node:child_process";
|
|
3287
3725
|
var CONARE_URL = "https://conare.ai";
|
|
3288
3726
|
var SERVER_NAME = "conare";
|
|
@@ -3295,11 +3733,13 @@ var MCP_TARGETS = [
|
|
|
3295
3733
|
{ id: "cline", label: "Cline", defaultSelected: false },
|
|
3296
3734
|
{ id: "zed", label: "Zed", defaultSelected: false },
|
|
3297
3735
|
{ id: "openclaw", label: "OpenClaw", defaultSelected: false },
|
|
3298
|
-
{ id: "antigravity", label: "Antigravity", defaultSelected: false }
|
|
3736
|
+
{ id: "antigravity", label: "Antigravity", defaultSelected: false },
|
|
3737
|
+
{ id: "opencode", label: "OpenCode", defaultSelected: false },
|
|
3738
|
+
{ id: "grok", label: "Grok", defaultSelected: false }
|
|
3299
3739
|
];
|
|
3300
3740
|
function readJsonFile(path) {
|
|
3301
3741
|
try {
|
|
3302
|
-
return JSON.parse(
|
|
3742
|
+
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
3303
3743
|
} catch {
|
|
3304
3744
|
return {};
|
|
3305
3745
|
}
|
|
@@ -3343,7 +3783,7 @@ function configureClaude(apiKey) {
|
|
|
3343
3783
|
}).status === 0) {
|
|
3344
3784
|
return "\x1B[32m✓\x1B[0m Claude Code";
|
|
3345
3785
|
}
|
|
3346
|
-
const claudeConfigPath =
|
|
3786
|
+
const claudeConfigPath = join9(homedir8(), ".claude.json");
|
|
3347
3787
|
const config = readJsonFile(claudeConfigPath);
|
|
3348
3788
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
3349
3789
|
config.mcpServers = {};
|
|
@@ -3353,7 +3793,7 @@ function configureClaude(apiKey) {
|
|
|
3353
3793
|
return "\x1B[32m✓\x1B[0m Claude Code (json fallback)";
|
|
3354
3794
|
}
|
|
3355
3795
|
function configureCodex(apiKey) {
|
|
3356
|
-
const configPath =
|
|
3796
|
+
const configPath = join9(homedir8(), ".codex", "config.toml");
|
|
3357
3797
|
if (spawnSync("codex", ["mcp", "add", SERVER_NAME, "--url", `${CONARE_URL}/mcp`, "--header", `Authorization: Bearer ${apiKey}`], {
|
|
3358
3798
|
stdio: "ignore",
|
|
3359
3799
|
shell: platform5() === "win32"
|
|
@@ -3362,7 +3802,7 @@ function configureCodex(apiKey) {
|
|
|
3362
3802
|
}
|
|
3363
3803
|
let toml = "";
|
|
3364
3804
|
try {
|
|
3365
|
-
toml =
|
|
3805
|
+
toml = readFileSync8(configPath, "utf-8");
|
|
3366
3806
|
} catch {}
|
|
3367
3807
|
const sectionHeader = `[mcp_servers.${SERVER_NAME}]`;
|
|
3368
3808
|
const newSection = [
|
|
@@ -3385,9 +3825,9 @@ ${newSection}
|
|
|
3385
3825
|
`;
|
|
3386
3826
|
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
3387
3827
|
writeFileSync2(configPath, result);
|
|
3388
|
-
const oldMcpJson =
|
|
3828
|
+
const oldMcpJson = join9(homedir8(), ".codex", "mcp.json");
|
|
3389
3829
|
try {
|
|
3390
|
-
if (
|
|
3830
|
+
if (existsSync9(oldMcpJson)) {
|
|
3391
3831
|
const old = readJsonFile(oldMcpJson);
|
|
3392
3832
|
const servers = old.mcpServers;
|
|
3393
3833
|
if (servers && (("conare" in servers) || ("conare-memory" in servers))) {
|
|
@@ -3404,7 +3844,7 @@ ${newSection}
|
|
|
3404
3844
|
return "\x1B[32m✓\x1B[0m Codex";
|
|
3405
3845
|
}
|
|
3406
3846
|
function configureCursor(apiKey) {
|
|
3407
|
-
const configPath =
|
|
3847
|
+
const configPath = join9(homedir8(), ".cursor", "mcp.json");
|
|
3408
3848
|
const config = readJsonFile(configPath);
|
|
3409
3849
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
3410
3850
|
config.mcpServers = {};
|
|
@@ -3417,7 +3857,7 @@ function configureCursor(apiKey) {
|
|
|
3417
3857
|
return "\x1B[32m✓\x1B[0m Cursor";
|
|
3418
3858
|
}
|
|
3419
3859
|
function configureWindsurf(apiKey) {
|
|
3420
|
-
const configPath =
|
|
3860
|
+
const configPath = join9(homedir8(), ".codeium", "windsurf", "mcp_config.json");
|
|
3421
3861
|
const config = readJsonFile(configPath);
|
|
3422
3862
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
3423
3863
|
config.mcpServers = {};
|
|
@@ -3433,11 +3873,11 @@ function configureVscode(apiKey) {
|
|
|
3433
3873
|
const os = platform5();
|
|
3434
3874
|
let configPath;
|
|
3435
3875
|
if (os === "darwin") {
|
|
3436
|
-
configPath =
|
|
3876
|
+
configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "mcp.json");
|
|
3437
3877
|
} else if (os === "win32") {
|
|
3438
|
-
configPath =
|
|
3878
|
+
configPath = join9(process.env.APPDATA || join9(homedir8(), "AppData", "Roaming"), "Code", "User", "mcp.json");
|
|
3439
3879
|
} else {
|
|
3440
|
-
configPath =
|
|
3880
|
+
configPath = join9(homedir8(), ".config", "Code", "User", "mcp.json");
|
|
3441
3881
|
}
|
|
3442
3882
|
const config = readJsonFile(configPath);
|
|
3443
3883
|
if (!config.servers || typeof config.servers !== "object") {
|
|
@@ -3455,11 +3895,11 @@ function configureCline(apiKey) {
|
|
|
3455
3895
|
const os = platform5();
|
|
3456
3896
|
let configPath;
|
|
3457
3897
|
if (os === "darwin") {
|
|
3458
|
-
configPath =
|
|
3898
|
+
configPath = join9(homedir8(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
3459
3899
|
} else if (os === "win32") {
|
|
3460
|
-
configPath =
|
|
3900
|
+
configPath = join9(process.env.APPDATA || join9(homedir8(), "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
3461
3901
|
} else {
|
|
3462
|
-
configPath =
|
|
3902
|
+
configPath = join9(homedir8(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
3463
3903
|
}
|
|
3464
3904
|
const config = readJsonFile(configPath);
|
|
3465
3905
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
@@ -3477,9 +3917,9 @@ function configureZed(apiKey) {
|
|
|
3477
3917
|
const os = platform5();
|
|
3478
3918
|
let configPath;
|
|
3479
3919
|
if (os === "darwin") {
|
|
3480
|
-
configPath =
|
|
3920
|
+
configPath = join9(homedir8(), ".zed", "settings.json");
|
|
3481
3921
|
} else {
|
|
3482
|
-
configPath =
|
|
3922
|
+
configPath = join9(homedir8(), ".config", "zed", "settings.json");
|
|
3483
3923
|
}
|
|
3484
3924
|
const config = readJsonFile(configPath);
|
|
3485
3925
|
if (!config.context_servers || typeof config.context_servers !== "object") {
|
|
@@ -3500,7 +3940,7 @@ function configureZed(apiKey) {
|
|
|
3500
3940
|
return "\x1B[32m✓\x1B[0m Zed";
|
|
3501
3941
|
}
|
|
3502
3942
|
function configureOpenclaw(apiKey) {
|
|
3503
|
-
const configPath =
|
|
3943
|
+
const configPath = join9(homedir8(), ".openclaw", "openclaw.json");
|
|
3504
3944
|
const config = readJsonFile(configPath);
|
|
3505
3945
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
3506
3946
|
config.mcpServers = {};
|
|
@@ -3514,7 +3954,7 @@ function configureOpenclaw(apiKey) {
|
|
|
3514
3954
|
return "\x1B[32m✓\x1B[0m OpenClaw";
|
|
3515
3955
|
}
|
|
3516
3956
|
function configureAntigravity(apiKey) {
|
|
3517
|
-
const configPath =
|
|
3957
|
+
const configPath = join9(homedir8(), ".gemini", "antigravity", "mcp_config.json");
|
|
3518
3958
|
const config = readJsonFile(configPath);
|
|
3519
3959
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
3520
3960
|
config.mcpServers = {};
|
|
@@ -3526,6 +3966,49 @@ function configureAntigravity(apiKey) {
|
|
|
3526
3966
|
writeJsonFile(configPath, config);
|
|
3527
3967
|
return "\x1B[32m✓\x1B[0m Antigravity";
|
|
3528
3968
|
}
|
|
3969
|
+
function configureOpencode(apiKey) {
|
|
3970
|
+
const configPath = join9(homedir8(), ".config", "opencode", "opencode.json");
|
|
3971
|
+
const config = readJsonFile(configPath);
|
|
3972
|
+
if (!config.mcp || typeof config.mcp !== "object") {
|
|
3973
|
+
config.mcp = {};
|
|
3974
|
+
}
|
|
3975
|
+
config.mcp[SERVER_NAME] = {
|
|
3976
|
+
type: "remote",
|
|
3977
|
+
url: `${CONARE_URL}/mcp`,
|
|
3978
|
+
enabled: true,
|
|
3979
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3980
|
+
};
|
|
3981
|
+
if (!config["$schema"])
|
|
3982
|
+
config["$schema"] = "https://opencode.ai/config.json";
|
|
3983
|
+
writeJsonFile(configPath, config);
|
|
3984
|
+
return "\x1B[32m✓\x1B[0m OpenCode";
|
|
3985
|
+
}
|
|
3986
|
+
function configureGrok(apiKey) {
|
|
3987
|
+
const configPath = join9(homedir8(), ".grok", "config.toml");
|
|
3988
|
+
let toml = "";
|
|
3989
|
+
try {
|
|
3990
|
+
toml = readFileSync8(configPath, "utf-8");
|
|
3991
|
+
} catch {}
|
|
3992
|
+
const newSection = [
|
|
3993
|
+
`[mcp_servers.${SERVER_NAME}]`,
|
|
3994
|
+
`url = "${CONARE_URL}/mcp"`,
|
|
3995
|
+
`enabled = true`,
|
|
3996
|
+
`headers = { "Authorization" = "Bearer ${apiKey}" }`
|
|
3997
|
+
].join(`
|
|
3998
|
+
`);
|
|
3999
|
+
const sectionRegex = new RegExp(`\\[mcp_servers\\.${SERVER_NAME}(?:\\.[^\\]]*)?\\][^\\[]*`, "g");
|
|
4000
|
+
const cleaned = toml.replace(sectionRegex, "").replace(/\n{3,}/g, `
|
|
4001
|
+
|
|
4002
|
+
`).trim();
|
|
4003
|
+
const result = cleaned ? `${cleaned}
|
|
4004
|
+
|
|
4005
|
+
${newSection}
|
|
4006
|
+
` : `${newSection}
|
|
4007
|
+
`;
|
|
4008
|
+
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
4009
|
+
writeFileSync2(configPath, result);
|
|
4010
|
+
return "\x1B[32m✓\x1B[0m Grok";
|
|
4011
|
+
}
|
|
3529
4012
|
var CLIENT_CONFIGURATORS = {
|
|
3530
4013
|
claude: configureClaude,
|
|
3531
4014
|
codex: configureCodex,
|
|
@@ -3535,7 +4018,9 @@ var CLIENT_CONFIGURATORS = {
|
|
|
3535
4018
|
cline: configureCline,
|
|
3536
4019
|
zed: configureZed,
|
|
3537
4020
|
openclaw: configureOpenclaw,
|
|
3538
|
-
antigravity: configureAntigravity
|
|
4021
|
+
antigravity: configureAntigravity,
|
|
4022
|
+
opencode: configureOpencode,
|
|
4023
|
+
grok: configureGrok
|
|
3539
4024
|
};
|
|
3540
4025
|
var SKILL_MD = `---
|
|
3541
4026
|
name: conare
|
|
@@ -3645,14 +4130,14 @@ The wizard handles everything: account creation, API key, MCP configuration, bac
|
|
|
3645
4130
|
For manual setup, visit [conare.ai](https://conare.ai).
|
|
3646
4131
|
`;
|
|
3647
4132
|
function installSkill() {
|
|
3648
|
-
const skillDir =
|
|
4133
|
+
const skillDir = join9(homedir8(), ".agents", "skills", "conare");
|
|
3649
4134
|
mkdirSync2(skillDir, { recursive: true });
|
|
3650
|
-
writeFileSync2(
|
|
3651
|
-
const claudeSkillsDir =
|
|
3652
|
-
const claudeSkillDir =
|
|
4135
|
+
writeFileSync2(join9(skillDir, "SKILL.md"), SKILL_MD);
|
|
4136
|
+
const claudeSkillsDir = join9(homedir8(), ".claude", "skills");
|
|
4137
|
+
const claudeSkillDir = join9(claudeSkillsDir, "conare");
|
|
3653
4138
|
try {
|
|
3654
|
-
if (
|
|
3655
|
-
if (
|
|
4139
|
+
if (existsSync9(claudeSkillsDir)) {
|
|
4140
|
+
if (existsSync9(claudeSkillDir)) {
|
|
3656
4141
|
try {
|
|
3657
4142
|
if (readlinkSync(claudeSkillDir) === skillDir)
|
|
3658
4143
|
return "\x1B[32m✓\x1B[0m Agent Skill";
|
|
@@ -3683,15 +4168,15 @@ function configureMcp(apiKey, targets = ["claude", "codex"]) {
|
|
|
3683
4168
|
}
|
|
3684
4169
|
|
|
3685
4170
|
// src/config.ts
|
|
3686
|
-
import { existsSync as
|
|
3687
|
-
import { join as
|
|
3688
|
-
import { homedir as
|
|
4171
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4172
|
+
import { join as join10 } from "node:path";
|
|
4173
|
+
import { homedir as homedir9 } from "node:os";
|
|
3689
4174
|
function readConfig() {
|
|
3690
4175
|
const configPath = getConfigPath();
|
|
3691
4176
|
try {
|
|
3692
|
-
if (!
|
|
4177
|
+
if (!existsSync10(configPath))
|
|
3693
4178
|
return {};
|
|
3694
|
-
return JSON.parse(
|
|
4179
|
+
return JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
3695
4180
|
} catch {
|
|
3696
4181
|
return {};
|
|
3697
4182
|
}
|
|
@@ -3702,10 +4187,10 @@ function writeConfig(config) {
|
|
|
3702
4187
|
`, { mode: 384 });
|
|
3703
4188
|
}
|
|
3704
4189
|
function getConfigDir() {
|
|
3705
|
-
return
|
|
4190
|
+
return join10(homedir9(), ".conare");
|
|
3706
4191
|
}
|
|
3707
4192
|
function getConfigPath() {
|
|
3708
|
-
return
|
|
4193
|
+
return join10(getConfigDir(), "config.json");
|
|
3709
4194
|
}
|
|
3710
4195
|
function saveApiKey(apiKey) {
|
|
3711
4196
|
const config = readConfig();
|
|
@@ -3725,7 +4210,7 @@ function clearSavedApiKey() {
|
|
|
3725
4210
|
delete config.key;
|
|
3726
4211
|
if (Object.keys(config).length === 0) {
|
|
3727
4212
|
const configPath = getConfigPath();
|
|
3728
|
-
if (
|
|
4213
|
+
if (existsSync10(configPath))
|
|
3729
4214
|
unlinkSync(configPath);
|
|
3730
4215
|
} else {
|
|
3731
4216
|
writeConfig(config);
|
|
@@ -3744,19 +4229,19 @@ function stripLegacyTeamConfig(config) {
|
|
|
3744
4229
|
}
|
|
3745
4230
|
|
|
3746
4231
|
// src/sync.ts
|
|
3747
|
-
import { existsSync as
|
|
3748
|
-
import { join as
|
|
3749
|
-
import { homedir as
|
|
4232
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync10, chmodSync, cpSync, rmSync as rmSync2, symlinkSync as symlinkSync2, readlinkSync as readlinkSync2, appendFileSync } from "node:fs";
|
|
4233
|
+
import { join as join11, dirname as dirname3 } from "node:path";
|
|
4234
|
+
import { homedir as homedir10, platform as platform6 } from "node:os";
|
|
3750
4235
|
import { execSync as execSync3 } from "node:child_process";
|
|
3751
4236
|
import { createRequire as createRequire3 } from "node:module";
|
|
3752
|
-
var CONARE_DIR =
|
|
3753
|
-
var BIN_DIR =
|
|
3754
|
-
var CONFIG_PATH =
|
|
4237
|
+
var CONARE_DIR = join11(homedir10(), ".conare");
|
|
4238
|
+
var BIN_DIR = join11(CONARE_DIR, "bin");
|
|
4239
|
+
var CONFIG_PATH = join11(CONARE_DIR, "config.json");
|
|
3755
4240
|
var PLIST_LABEL = "ai.conare.ingest";
|
|
3756
|
-
var PLIST_PATH =
|
|
3757
|
-
var SYSTEMD_DIR =
|
|
3758
|
-
var SYSTEMD_SERVICE =
|
|
3759
|
-
var SYSTEMD_TIMER =
|
|
4241
|
+
var PLIST_PATH = join11(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
4242
|
+
var SYSTEMD_DIR = join11(homedir10(), ".config", "systemd", "user");
|
|
4243
|
+
var SYSTEMD_SERVICE = join11(SYSTEMD_DIR, "conare-sync.service");
|
|
4244
|
+
var SYSTEMD_TIMER = join11(SYSTEMD_DIR, "conare-sync.timer");
|
|
3760
4245
|
var TASK_NAME = "ConareMemorySync";
|
|
3761
4246
|
var RUN_VBS = `Set WshShell = CreateObject("WScript.Shell")
|
|
3762
4247
|
WshShell.Run """" & CreateObject("WScript.Shell").ExpandEnvironmentStrings("%USERPROFILE%") & "\\.conare\\bin\\run.cmd" & """", 0, True
|
|
@@ -3901,38 +4386,38 @@ function persistBinary(apiKey) {
|
|
|
3901
4386
|
if (!cliEntry) {
|
|
3902
4387
|
throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
|
|
3903
4388
|
}
|
|
3904
|
-
const dest =
|
|
3905
|
-
const content =
|
|
4389
|
+
const dest = join11(BIN_DIR, "conare-ingest.mjs");
|
|
4390
|
+
const content = readFileSync10(cliEntry, "utf-8");
|
|
3906
4391
|
writeFileSync4(dest, content);
|
|
3907
4392
|
for (const moduleName of ["sql.js", "better-sqlite3", "bindings", "file-uri-to-path"]) {
|
|
3908
4393
|
try {
|
|
3909
4394
|
copyNodeModule(moduleName);
|
|
3910
4395
|
} catch {}
|
|
3911
4396
|
}
|
|
3912
|
-
const runShPath =
|
|
4397
|
+
const runShPath = join11(BIN_DIR, "run.sh");
|
|
3913
4398
|
writeFileSync4(runShPath, RUN_SH);
|
|
3914
4399
|
try {
|
|
3915
4400
|
chmodSync(runShPath, 493);
|
|
3916
4401
|
} catch {}
|
|
3917
|
-
const runCmdPath =
|
|
4402
|
+
const runCmdPath = join11(BIN_DIR, "run.cmd");
|
|
3918
4403
|
writeFileSync4(runCmdPath, RUN_CMD);
|
|
3919
|
-
const runVbsPath =
|
|
4404
|
+
const runVbsPath = join11(BIN_DIR, "run.vbs");
|
|
3920
4405
|
writeFileSync4(runVbsPath, RUN_VBS);
|
|
3921
4406
|
let existing = {};
|
|
3922
|
-
if (
|
|
4407
|
+
if (existsSync11(CONFIG_PATH)) {
|
|
3923
4408
|
try {
|
|
3924
|
-
existing = JSON.parse(
|
|
4409
|
+
existing = JSON.parse(readFileSync10(CONFIG_PATH, "utf-8"));
|
|
3925
4410
|
} catch {}
|
|
3926
4411
|
}
|
|
3927
4412
|
writeFileSync4(CONFIG_PATH, JSON.stringify(mergeApiKeyIntoConfig(existing, apiKey), null, 2) + `
|
|
3928
4413
|
`, { mode: 384 });
|
|
3929
4414
|
}
|
|
3930
4415
|
function isValidJsBundle(path) {
|
|
3931
|
-
if (!
|
|
4416
|
+
if (!existsSync11(path))
|
|
3932
4417
|
return false;
|
|
3933
4418
|
if (path.endsWith(".ts") || path.endsWith(".tsx"))
|
|
3934
4419
|
return false;
|
|
3935
|
-
const head =
|
|
4420
|
+
const head = readFileSync10(path, "utf-8").slice(0, 2000);
|
|
3936
4421
|
if (/\btype\s+\{/.test(head) || /,\s*type\s+\w+/.test(head))
|
|
3937
4422
|
return false;
|
|
3938
4423
|
return true;
|
|
@@ -3940,8 +4425,8 @@ function isValidJsBundle(path) {
|
|
|
3940
4425
|
function findCliBundle() {
|
|
3941
4426
|
const dir = dirname3(new URL(import.meta.url).pathname);
|
|
3942
4427
|
const distCandidates = [
|
|
3943
|
-
|
|
3944
|
-
|
|
4428
|
+
join11(dir, "index.js"),
|
|
4429
|
+
join11(dir, "..", "dist", "index.js")
|
|
3945
4430
|
];
|
|
3946
4431
|
for (const c of distCandidates) {
|
|
3947
4432
|
if (isValidJsBundle(c))
|
|
@@ -3958,12 +4443,12 @@ function findNodeModule(name) {
|
|
|
3958
4443
|
return dirname3(require2.resolve(`${name}/package.json`));
|
|
3959
4444
|
} catch {}
|
|
3960
4445
|
const candidates = [
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
4446
|
+
join11(process.cwd(), "node_modules", name),
|
|
4447
|
+
join11(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", name),
|
|
4448
|
+
join11(dirname3(new URL(import.meta.url).pathname), "..", "..", "node_modules", name)
|
|
3964
4449
|
];
|
|
3965
4450
|
for (const c of candidates) {
|
|
3966
|
-
if (
|
|
4451
|
+
if (existsSync11(c))
|
|
3967
4452
|
return c;
|
|
3968
4453
|
}
|
|
3969
4454
|
return null;
|
|
@@ -3972,7 +4457,7 @@ function copyNodeModule(name) {
|
|
|
3972
4457
|
const sourceDir = findNodeModule(name);
|
|
3973
4458
|
if (!sourceDir)
|
|
3974
4459
|
return;
|
|
3975
|
-
const targetDir =
|
|
4460
|
+
const targetDir = join11(BIN_DIR, "node_modules", name);
|
|
3976
4461
|
rmSync2(targetDir, { recursive: true, force: true });
|
|
3977
4462
|
mkdirSync4(dirname3(targetDir), { recursive: true });
|
|
3978
4463
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
@@ -4007,7 +4492,7 @@ function makeLaunchdPlist(intervalSeconds) {
|
|
|
4007
4492
|
<key>ProgramArguments</key>
|
|
4008
4493
|
<array>
|
|
4009
4494
|
<string>/bin/bash</string>
|
|
4010
|
-
<string>${
|
|
4495
|
+
<string>${join11(BIN_DIR, "run.sh")}</string>
|
|
4011
4496
|
</array>
|
|
4012
4497
|
<key>StartInterval</key>
|
|
4013
4498
|
<integer>${intervalSeconds}</integer>
|
|
@@ -4016,7 +4501,7 @@ function makeLaunchdPlist(intervalSeconds) {
|
|
|
4016
4501
|
<key>ProcessType</key>
|
|
4017
4502
|
<string>Background</string>
|
|
4018
4503
|
<key>StandardErrorPath</key>
|
|
4019
|
-
<string>${
|
|
4504
|
+
<string>${join11(CONARE_DIR, "ingest.log")}</string>
|
|
4020
4505
|
</dict>
|
|
4021
4506
|
</plist>
|
|
4022
4507
|
`;
|
|
@@ -4069,7 +4554,7 @@ function clampCronInterval(intervalMinutes) {
|
|
|
4069
4554
|
}
|
|
4070
4555
|
function setupCron(intervalMinutes) {
|
|
4071
4556
|
const clamped = clampCronInterval(intervalMinutes);
|
|
4072
|
-
const cronCmd = `"${
|
|
4557
|
+
const cronCmd = `"${homedir10()}/.conare/bin/run.sh"`;
|
|
4073
4558
|
const cronLine = `*/${clamped} * * * * ${cronCmd} # conare-sync`;
|
|
4074
4559
|
try {
|
|
4075
4560
|
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
@@ -4110,7 +4595,7 @@ function removeCronEntry() {
|
|
|
4110
4595
|
function installGlobalCommand() {
|
|
4111
4596
|
const isWindows = platform6() === "win32";
|
|
4112
4597
|
if (isWindows) {
|
|
4113
|
-
const wrapper2 =
|
|
4598
|
+
const wrapper2 = join11(BIN_DIR, "conare.cmd");
|
|
4114
4599
|
const content2 = `@echo off\r
|
|
4115
4600
|
where node >nul 2>nul\r
|
|
4116
4601
|
if errorlevel 1 (\r
|
|
@@ -4132,7 +4617,7 @@ node "%USERPROFILE%\\.conare\\bin\\conare-ingest.mjs" %*\r
|
|
|
4132
4617
|
return `Global command: add ${binDirWin} to your PATH manually`;
|
|
4133
4618
|
}
|
|
4134
4619
|
}
|
|
4135
|
-
const wrapper =
|
|
4620
|
+
const wrapper = join11(BIN_DIR, "conare");
|
|
4136
4621
|
const content = `#!/bin/bash
|
|
4137
4622
|
# Conare global command — runs the persisted CLI bundle
|
|
4138
4623
|
CONARE_DIR="$HOME/.conare"
|
|
@@ -4153,7 +4638,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
|
|
|
4153
4638
|
chmodSync(wrapper, 493);
|
|
4154
4639
|
const symlinkTarget = "/usr/local/bin/conare";
|
|
4155
4640
|
try {
|
|
4156
|
-
if (
|
|
4641
|
+
if (existsSync11(symlinkTarget)) {
|
|
4157
4642
|
try {
|
|
4158
4643
|
const existing = readlinkSync2(symlinkTarget);
|
|
4159
4644
|
if (existing === wrapper)
|
|
@@ -4171,7 +4656,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
|
|
|
4171
4656
|
const shellProfile = getShellProfile();
|
|
4172
4657
|
if (shellProfile) {
|
|
4173
4658
|
try {
|
|
4174
|
-
const profileContent =
|
|
4659
|
+
const profileContent = existsSync11(shellProfile) ? readFileSync10(shellProfile, "utf-8") : "";
|
|
4175
4660
|
const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
|
|
4176
4661
|
if (!profileContent.includes(".conare/bin")) {
|
|
4177
4662
|
appendFileSync(shellProfile, `
|
|
@@ -4189,24 +4674,24 @@ ${exportLine}
|
|
|
4189
4674
|
}
|
|
4190
4675
|
}
|
|
4191
4676
|
function getShellProfile() {
|
|
4192
|
-
const home =
|
|
4677
|
+
const home = homedir10();
|
|
4193
4678
|
const shell = process.env.SHELL || "";
|
|
4194
4679
|
if (shell.includes("zsh"))
|
|
4195
|
-
return
|
|
4680
|
+
return join11(home, ".zshrc");
|
|
4196
4681
|
if (shell.includes("bash")) {
|
|
4197
|
-
const profile =
|
|
4198
|
-
if (platform6() === "darwin" &&
|
|
4682
|
+
const profile = join11(home, ".bash_profile");
|
|
4683
|
+
if (platform6() === "darwin" && existsSync11(profile))
|
|
4199
4684
|
return profile;
|
|
4200
|
-
return
|
|
4685
|
+
return join11(home, ".bashrc");
|
|
4201
4686
|
}
|
|
4202
|
-
if (
|
|
4203
|
-
return
|
|
4204
|
-
if (
|
|
4205
|
-
return
|
|
4687
|
+
if (existsSync11(join11(home, ".zshrc")))
|
|
4688
|
+
return join11(home, ".zshrc");
|
|
4689
|
+
if (existsSync11(join11(home, ".bashrc")))
|
|
4690
|
+
return join11(home, ".bashrc");
|
|
4206
4691
|
return null;
|
|
4207
4692
|
}
|
|
4208
4693
|
function setupWindows(intervalMinutes) {
|
|
4209
|
-
const runVbs =
|
|
4694
|
+
const runVbs = join11(BIN_DIR, "run.vbs").replace(/\//g, "\\");
|
|
4210
4695
|
try {
|
|
4211
4696
|
execSync3(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
4212
4697
|
} catch {}
|
|
@@ -4346,18 +4831,18 @@ function detectMechanism() {
|
|
|
4346
4831
|
}
|
|
4347
4832
|
function syncStatus() {
|
|
4348
4833
|
const mechanism = detectMechanism();
|
|
4349
|
-
const logPath =
|
|
4834
|
+
const logPath = join11(CONARE_DIR, "ingest.log");
|
|
4350
4835
|
let parsed = { lastSyncIso: null, lastSyncError: null };
|
|
4351
|
-
if (
|
|
4836
|
+
if (existsSync11(logPath)) {
|
|
4352
4837
|
try {
|
|
4353
|
-
parsed = parseLastSync(
|
|
4838
|
+
parsed = parseLastSync(readFileSync10(logPath, "utf-8"));
|
|
4354
4839
|
} catch {}
|
|
4355
4840
|
}
|
|
4356
4841
|
return {
|
|
4357
4842
|
timerInstalled: syncTimerInstalled(),
|
|
4358
4843
|
mechanism,
|
|
4359
|
-
binaryPersisted:
|
|
4360
|
-
configPresent:
|
|
4844
|
+
binaryPersisted: existsSync11(join11(BIN_DIR, "conare-ingest.mjs")),
|
|
4845
|
+
configPresent: existsSync11(CONFIG_PATH),
|
|
4361
4846
|
lastSyncIso: parsed.lastSyncIso,
|
|
4362
4847
|
lastSyncError: parsed.lastSyncError
|
|
4363
4848
|
};
|
|
@@ -4367,7 +4852,7 @@ function removeSyncTimer() {
|
|
|
4367
4852
|
const os = platform6();
|
|
4368
4853
|
if (os === "darwin") {
|
|
4369
4854
|
bootoutLaunchAgent();
|
|
4370
|
-
if (
|
|
4855
|
+
if (existsSync11(PLIST_PATH)) {
|
|
4371
4856
|
unlinkSync2(PLIST_PATH);
|
|
4372
4857
|
messages.push("Removed launchd agent");
|
|
4373
4858
|
}
|
|
@@ -4382,9 +4867,9 @@ function removeSyncTimer() {
|
|
|
4382
4867
|
try {
|
|
4383
4868
|
execSync3("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
|
|
4384
4869
|
} catch {}
|
|
4385
|
-
if (
|
|
4870
|
+
if (existsSync11(SYSTEMD_SERVICE))
|
|
4386
4871
|
unlinkSync2(SYSTEMD_SERVICE);
|
|
4387
|
-
if (
|
|
4872
|
+
if (existsSync11(SYSTEMD_TIMER))
|
|
4388
4873
|
unlinkSync2(SYSTEMD_TIMER);
|
|
4389
4874
|
try {
|
|
4390
4875
|
execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
@@ -4397,19 +4882,19 @@ function removeSyncTimer() {
|
|
|
4397
4882
|
return messages;
|
|
4398
4883
|
}
|
|
4399
4884
|
function clearSyncLocks() {
|
|
4400
|
-
const lockDir =
|
|
4401
|
-
if (
|
|
4885
|
+
const lockDir = join11(CONARE_DIR, "sync.lock.d");
|
|
4886
|
+
if (existsSync11(lockDir))
|
|
4402
4887
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
4403
|
-
const lockFile =
|
|
4404
|
-
if (
|
|
4888
|
+
const lockFile = join11(CONARE_DIR, "sync.lock");
|
|
4889
|
+
if (existsSync11(lockFile))
|
|
4405
4890
|
unlinkSync2(lockFile);
|
|
4406
4891
|
}
|
|
4407
4892
|
function uninstallSync() {
|
|
4408
4893
|
const messages = removeSyncTimer();
|
|
4409
|
-
if (
|
|
4894
|
+
if (existsSync11(CONFIG_PATH))
|
|
4410
4895
|
unlinkSync2(CONFIG_PATH);
|
|
4411
4896
|
clearSyncLocks();
|
|
4412
|
-
if (
|
|
4897
|
+
if (existsSync11(BIN_DIR)) {
|
|
4413
4898
|
rmSync2(BIN_DIR, { recursive: true, force: true });
|
|
4414
4899
|
messages.push("Removed ~/.conare/bin/");
|
|
4415
4900
|
}
|
|
@@ -4421,11 +4906,23 @@ function uninstallSync() {
|
|
|
4421
4906
|
|
|
4422
4907
|
// src/index.ts
|
|
4423
4908
|
init_interactive();
|
|
4909
|
+
var nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
4910
|
+
if (nodeMajor < 20 || typeof fetch !== "function" || typeof crypto === "undefined") {
|
|
4911
|
+
console.error("");
|
|
4912
|
+
console.error(` Conare needs Node 20 or newer — you're running ${process.version}.`);
|
|
4913
|
+
console.error(" Install the current LTS from https://nodejs.org and re-run:");
|
|
4914
|
+
console.error("");
|
|
4915
|
+
console.error(" npx conare@latest");
|
|
4916
|
+
console.error("");
|
|
4917
|
+
process.exit(1);
|
|
4918
|
+
}
|
|
4424
4919
|
var CONARE_URL2 = "https://conare.ai";
|
|
4425
4920
|
var CHAT_CONTAINER_LABELS = {
|
|
4426
4921
|
"claude-chats": "Claude Code",
|
|
4427
4922
|
"codex-chats": "Codex",
|
|
4428
|
-
"cursor-chats": "Cursor"
|
|
4923
|
+
"cursor-chats": "Cursor",
|
|
4924
|
+
"opencode-chats": "OpenCode",
|
|
4925
|
+
"grok-chats": "Grok"
|
|
4429
4926
|
};
|
|
4430
4927
|
function getManifestFingerprint(memory) {
|
|
4431
4928
|
const metadata = memory.metadata;
|
|
@@ -4582,7 +5079,7 @@ Options:
|
|
|
4582
5079
|
--ingest-only Index memories without MCP configuration
|
|
4583
5080
|
--config-only Configure MCP only, skip indexing
|
|
4584
5081
|
--interactive Run guided setup prompts
|
|
4585
|
-
--source <name> Only index from: claude, codex, cursor
|
|
5082
|
+
--source <name> Only index from: claude, codex, cursor, opencode, grok
|
|
4586
5083
|
--wasm-dir <path> Path to sql.js module (for Cursor indexing)
|
|
4587
5084
|
|
|
4588
5085
|
Get your API key at https://conare.ai
|
|
@@ -4720,8 +5217,8 @@ async function main() {
|
|
|
4720
5217
|
let configFileKey;
|
|
4721
5218
|
if (opts.configFile) {
|
|
4722
5219
|
try {
|
|
4723
|
-
const { readFileSync:
|
|
4724
|
-
const raw = JSON.parse(
|
|
5220
|
+
const { readFileSync: readFileSync11 } = await import("node:fs");
|
|
5221
|
+
const raw = JSON.parse(readFileSync11(opts.configFile, "utf-8"));
|
|
4725
5222
|
configFileKey = raw.apiKey || raw.key;
|
|
4726
5223
|
if (!configFileKey) {
|
|
4727
5224
|
console.error(`Error: no apiKey/key found in ${opts.configFile}`);
|
|
@@ -4741,7 +5238,7 @@ async function main() {
|
|
|
4741
5238
|
let effectiveConfigOnly = opts.configOnly;
|
|
4742
5239
|
let effectiveIngestOnly = opts.ingestOnly;
|
|
4743
5240
|
let effectiveIndexPath = opts.indexPath;
|
|
4744
|
-
let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex"];
|
|
5241
|
+
let selectedSources = opts.source ? [opts.source] : ["claude", "cursor", "codex", "opencode", "grok"];
|
|
4745
5242
|
let apiKey = opts.key || configFileKey || process.env.CONARE_API_KEY || savedApiKey;
|
|
4746
5243
|
let interactiveTargets = MCP_TARGETS.map((target) => ({
|
|
4747
5244
|
id: target.id,
|
|
@@ -4788,7 +5285,7 @@ async function main() {
|
|
|
4788
5285
|
detectedCountApproximate: detected?.sessionCountApproximate
|
|
4789
5286
|
};
|
|
4790
5287
|
});
|
|
4791
|
-
const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor"]);
|
|
5288
|
+
const INGESTIBLE_SOURCES = new Set(["claude", "codex", "cursor", "opencode", "grok"]);
|
|
4792
5289
|
const ingestibleTargets = interactiveTargets.filter((t) => INGESTIBLE_SOURCES.has(t.id));
|
|
4793
5290
|
showDetectedApps(ingestibleTargets);
|
|
4794
5291
|
selectedSources = await selectChatSources(ingestibleTargets);
|
|
@@ -4836,7 +5333,7 @@ async function main() {
|
|
|
4836
5333
|
}
|
|
4837
5334
|
saveApiKey(apiKey);
|
|
4838
5335
|
if (auth.id)
|
|
4839
|
-
|
|
5336
|
+
setManifestScope(auth.id, apiKey);
|
|
4840
5337
|
if (!opts.force && !opts.dryRun) {
|
|
4841
5338
|
const remoteMemoryCount = await getRemoteMemoryCount(apiKey);
|
|
4842
5339
|
const localManifest = getIngested();
|
|
@@ -4858,8 +5355,8 @@ async function main() {
|
|
|
4858
5355
|
}
|
|
4859
5356
|
}
|
|
4860
5357
|
}
|
|
4861
|
-
if (!opts.wasmDir &&
|
|
4862
|
-
opts.wasmDir =
|
|
5358
|
+
if (!opts.wasmDir && existsSync12(join12(process.cwd(), "node_modules", "sql.js"))) {
|
|
5359
|
+
opts.wasmDir = join12(process.cwd(), "node_modules");
|
|
4863
5360
|
}
|
|
4864
5361
|
if (effectiveConfigOnly) {
|
|
4865
5362
|
if (!opts.dryRun) {
|
|
@@ -5006,11 +5503,11 @@ Nothing new to index.`);
|
|
|
5006
5503
|
if (!opts.quiet) {
|
|
5007
5504
|
const s = Y2();
|
|
5008
5505
|
s.start("Scanning Claude Code chats...");
|
|
5009
|
-
const { memories, filtered, deduped } = ingestClaude(
|
|
5506
|
+
const { memories, filtered, deduped } = ingestClaude();
|
|
5010
5507
|
allMemories.push(...memories);
|
|
5011
5508
|
s.stop(`Claude Code: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
5012
5509
|
} else {
|
|
5013
|
-
allMemories.push(...ingestClaude(
|
|
5510
|
+
allMemories.push(...ingestClaude().memories);
|
|
5014
5511
|
}
|
|
5015
5512
|
}
|
|
5016
5513
|
if (shouldIngest("codex") && tools.find((t) => t.name === "Codex")?.available) {
|
|
@@ -5029,12 +5526,34 @@ Nothing new to index.`);
|
|
|
5029
5526
|
const s = Y2();
|
|
5030
5527
|
s.start("Scanning Cursor chats...");
|
|
5031
5528
|
const cursorTool = tools.find((t) => t.name === "Cursor");
|
|
5032
|
-
const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir
|
|
5529
|
+
const { memories, filtered, deduped } = await ingestCursor(cursorTool.path, opts.wasmDir);
|
|
5033
5530
|
allMemories.push(...memories);
|
|
5034
5531
|
s.stop(`Cursor: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
5035
5532
|
} else {
|
|
5036
5533
|
const cursorTool = tools.find((t) => t.name === "Cursor");
|
|
5037
|
-
allMemories.push(...(await ingestCursor(cursorTool.path, opts.wasmDir
|
|
5534
|
+
allMemories.push(...(await ingestCursor(cursorTool.path, opts.wasmDir)).memories);
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
if (shouldIngest("opencode") && tools.find((t) => t.name === "OpenCode")?.available) {
|
|
5538
|
+
if (!opts.quiet) {
|
|
5539
|
+
const s = Y2();
|
|
5540
|
+
s.start("Scanning OpenCode chats...");
|
|
5541
|
+
const { memories, filtered, deduped } = ingestOpenCode();
|
|
5542
|
+
allMemories.push(...memories);
|
|
5543
|
+
s.stop(`OpenCode: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
5544
|
+
} else {
|
|
5545
|
+
allMemories.push(...ingestOpenCode().memories);
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
if (shouldIngest("grok") && tools.find((t) => t.name === "Grok")?.available) {
|
|
5549
|
+
if (!opts.quiet) {
|
|
5550
|
+
const s = Y2();
|
|
5551
|
+
s.start("Scanning Grok chats...");
|
|
5552
|
+
const { memories, filtered, deduped } = ingestGrok();
|
|
5553
|
+
allMemories.push(...memories);
|
|
5554
|
+
s.stop(`Grok: ${renderDiscoverySummary(memories.length, filtered, deduped)}`);
|
|
5555
|
+
} else {
|
|
5556
|
+
allMemories.push(...ingestGrok().memories);
|
|
5038
5557
|
}
|
|
5039
5558
|
}
|
|
5040
5559
|
allMemories.sort((a, b3) => {
|
|
@@ -5120,7 +5639,9 @@ Nothing new to index.`);
|
|
|
5120
5639
|
const successfulKeysBySource = {
|
|
5121
5640
|
claude: [],
|
|
5122
5641
|
codex: [],
|
|
5123
|
-
cursor: []
|
|
5642
|
+
cursor: [],
|
|
5643
|
+
opencode: [],
|
|
5644
|
+
grok: []
|
|
5124
5645
|
};
|
|
5125
5646
|
for (const result of results) {
|
|
5126
5647
|
if (!result.success)
|
|
@@ -5139,6 +5660,12 @@ Nothing new to index.`);
|
|
|
5139
5660
|
case "cursor-chats":
|
|
5140
5661
|
successfulKeysBySource.cursor.push(key);
|
|
5141
5662
|
break;
|
|
5663
|
+
case "opencode-chats":
|
|
5664
|
+
successfulKeysBySource.opencode.push(key);
|
|
5665
|
+
break;
|
|
5666
|
+
case "grok-chats":
|
|
5667
|
+
successfulKeysBySource.grok.push(key);
|
|
5668
|
+
break;
|
|
5142
5669
|
}
|
|
5143
5670
|
}
|
|
5144
5671
|
for (const [source, ids] of Object.entries(successfulKeysBySource)) {
|
|
@@ -5194,6 +5721,10 @@ Nothing new to index.`);
|
|
|
5194
5721
|
return "Codex";
|
|
5195
5722
|
if (t === "cursor")
|
|
5196
5723
|
return "Cursor";
|
|
5724
|
+
if (t === "opencode")
|
|
5725
|
+
return "OpenCode";
|
|
5726
|
+
if (t === "grok")
|
|
5727
|
+
return "Grok";
|
|
5197
5728
|
return t;
|
|
5198
5729
|
});
|
|
5199
5730
|
const toolList = configuredTools.length > 0 ? configuredTools.join(", ") : "your AI tools";
|