conare 0.5.6 → 0.5.7
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 +521 -446
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,10 +48,10 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
48
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
49
49
|
|
|
50
50
|
// src/ingest/shared.ts
|
|
51
|
-
import { existsSync
|
|
51
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
52
52
|
import { createHash } from "node:crypto";
|
|
53
|
-
import { join
|
|
54
|
-
import { homedir
|
|
53
|
+
import { join } from "node:path";
|
|
54
|
+
import { homedir } from "node:os";
|
|
55
55
|
function fitContent(header, rounds) {
|
|
56
56
|
const buildContent = (maxUser) => {
|
|
57
57
|
const body = rounds.map((r) => {
|
|
@@ -112,8 +112,8 @@ function createContentHash(content) {
|
|
|
112
112
|
}
|
|
113
113
|
function getIngested() {
|
|
114
114
|
try {
|
|
115
|
-
if (
|
|
116
|
-
return JSON.parse(
|
|
115
|
+
if (existsSync(MANIFEST_PATH)) {
|
|
116
|
+
return JSON.parse(readFileSync(MANIFEST_PATH, "utf-8"));
|
|
117
117
|
}
|
|
118
118
|
} catch {}
|
|
119
119
|
return {};
|
|
@@ -124,8 +124,8 @@ function markIngested(source, sessionIds) {
|
|
|
124
124
|
for (const id of sessionIds)
|
|
125
125
|
existing.add(id);
|
|
126
126
|
manifest[source] = [...existing];
|
|
127
|
-
const dir =
|
|
128
|
-
if (!
|
|
127
|
+
const dir = join(homedir(), ".conare");
|
|
128
|
+
if (!existsSync(dir))
|
|
129
129
|
mkdirSync(dir, { recursive: true });
|
|
130
130
|
writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
131
131
|
}
|
|
@@ -141,14 +141,14 @@ function clearIngested(source) {
|
|
|
141
141
|
for (const key of Object.keys(manifest))
|
|
142
142
|
delete manifest[key];
|
|
143
143
|
}
|
|
144
|
-
const dir =
|
|
145
|
-
if (!
|
|
144
|
+
const dir = join(homedir(), ".conare");
|
|
145
|
+
if (!existsSync(dir))
|
|
146
146
|
mkdirSync(dir, { recursive: true });
|
|
147
147
|
writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
148
148
|
}
|
|
149
149
|
var MANIFEST_PATH, MAX_MEMORY_CONTENT = 200000, TRUNCATED_USER_MSG = 3000, MIN_SUBSTANTIVE = 200, NARRATION_RE;
|
|
150
150
|
var init_shared = __esm(() => {
|
|
151
|
-
MANIFEST_PATH =
|
|
151
|
+
MANIFEST_PATH = join(homedir(), ".conare", "ingested.json");
|
|
152
152
|
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))/;
|
|
153
153
|
});
|
|
154
154
|
|
|
@@ -161,16 +161,16 @@ __export(exports_codebase, {
|
|
|
161
161
|
detectProjectName: () => detectProjectName
|
|
162
162
|
});
|
|
163
163
|
import { createHash as createHash2 } from "node:crypto";
|
|
164
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
164
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync2, existsSync as existsSync6 } from "node:fs";
|
|
165
165
|
import { join as join6, relative, extname, resolve, basename as basename3 } from "node:path";
|
|
166
166
|
import { execSync as execSync2 } from "node:child_process";
|
|
167
167
|
function parseGitignore(rootPath) {
|
|
168
168
|
const patterns = new Set;
|
|
169
169
|
const gitignorePath = join6(rootPath, ".gitignore");
|
|
170
|
-
if (!
|
|
170
|
+
if (!existsSync6(gitignorePath))
|
|
171
171
|
return patterns;
|
|
172
172
|
try {
|
|
173
|
-
const content =
|
|
173
|
+
const content = readFileSync5(gitignorePath, "utf-8");
|
|
174
174
|
for (const line of content.split(`
|
|
175
175
|
`)) {
|
|
176
176
|
const trimmed = line.trim();
|
|
@@ -206,15 +206,15 @@ ${content}
|
|
|
206
206
|
function detectProjectName(rootPath) {
|
|
207
207
|
const readers = [
|
|
208
208
|
() => {
|
|
209
|
-
const pkg = JSON.parse(
|
|
209
|
+
const pkg = JSON.parse(readFileSync5(join6(rootPath, "package.json"), "utf-8"));
|
|
210
210
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
211
211
|
},
|
|
212
212
|
() => {
|
|
213
|
-
const content =
|
|
213
|
+
const content = readFileSync5(join6(rootPath, "Cargo.toml"), "utf-8");
|
|
214
214
|
return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
215
215
|
},
|
|
216
216
|
() => {
|
|
217
|
-
const content =
|
|
217
|
+
const content = readFileSync5(join6(rootPath, "pyproject.toml"), "utf-8");
|
|
218
218
|
return content.match(/^name\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
219
219
|
}
|
|
220
220
|
];
|
|
@@ -311,7 +311,7 @@ function indexCodebase(rootPath, options = {}) {
|
|
|
311
311
|
}
|
|
312
312
|
let raw;
|
|
313
313
|
try {
|
|
314
|
-
raw =
|
|
314
|
+
raw = readFileSync5(fullPath, "utf-8");
|
|
315
315
|
} catch {
|
|
316
316
|
skipped++;
|
|
317
317
|
continue;
|
|
@@ -1790,10 +1790,10 @@ __export(exports_interactive, {
|
|
|
1790
1790
|
finishSetup: () => finishSetup,
|
|
1791
1791
|
confirmBackgroundSync: () => confirmBackgroundSync
|
|
1792
1792
|
});
|
|
1793
|
-
function formatDetectedCount(count) {
|
|
1793
|
+
function formatDetectedCount(count, approximate = false) {
|
|
1794
1794
|
if (count === undefined)
|
|
1795
1795
|
return "available";
|
|
1796
|
-
return `${count.toLocaleString()} chats`;
|
|
1796
|
+
return `${approximate ? "~" : ""}${count.toLocaleString()} chats`;
|
|
1797
1797
|
}
|
|
1798
1798
|
function ensureValue(value) {
|
|
1799
1799
|
if (pD(value)) {
|
|
@@ -1809,7 +1809,7 @@ function finishSetup() {
|
|
|
1809
1809
|
Se("Starting setup...");
|
|
1810
1810
|
}
|
|
1811
1811
|
function showDetectedApps(targets) {
|
|
1812
|
-
Me(targets.map((target) => `• ${target.label}: ${target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)}`).join(`
|
|
1812
|
+
Me(targets.map((target) => `• ${target.label}: ${target.available === false ? "not detected" : formatDetectedCount(target.detectedCount, target.detectedCountApproximate)}`).join(`
|
|
1813
1813
|
`), "Detected apps");
|
|
1814
1814
|
}
|
|
1815
1815
|
async function promptApiKey(options) {
|
|
@@ -1874,7 +1874,7 @@ async function selectChatSources(targets) {
|
|
|
1874
1874
|
options: targets.map((target) => ({
|
|
1875
1875
|
value: target.id,
|
|
1876
1876
|
label: target.label,
|
|
1877
|
-
hint: target.available === false ? "not detected" : formatDetectedCount(target.detectedCount)
|
|
1877
|
+
hint: target.available === false ? "not detected" : formatDetectedCount(target.detectedCount, target.detectedCountApproximate)
|
|
1878
1878
|
}))
|
|
1879
1879
|
}));
|
|
1880
1880
|
}
|
|
@@ -1906,271 +1906,23 @@ var init_interactive = __esm(() => {
|
|
|
1906
1906
|
});
|
|
1907
1907
|
|
|
1908
1908
|
// src/index.ts
|
|
1909
|
-
import { existsSync as
|
|
1909
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
1910
1910
|
import { join as join10 } from "node:path";
|
|
1911
1911
|
|
|
1912
1912
|
// src/detect.ts
|
|
1913
|
-
import { existsSync, readdirSync
|
|
1914
|
-
import { join } from "node:path";
|
|
1915
|
-
import { homedir, platform } from "node:os";
|
|
1916
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
1917
|
-
function countJsonlFiles(dir) {
|
|
1918
|
-
let count = 0;
|
|
1919
|
-
try {
|
|
1920
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1921
|
-
if (entry.isDirectory()) {
|
|
1922
|
-
count += countJsonlFiles(join(dir, entry.name));
|
|
1923
|
-
} else if (entry.name.endsWith(".jsonl")) {
|
|
1924
|
-
count++;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
} catch {}
|
|
1928
|
-
return count;
|
|
1929
|
-
}
|
|
1930
|
-
async function countCursorSessions(dbPath) {
|
|
1931
|
-
try {
|
|
1932
|
-
const require2 = createRequire2(import.meta.url);
|
|
1933
|
-
const initSqlJs = require2("sql.js");
|
|
1934
|
-
const SQL = await initSqlJs();
|
|
1935
|
-
const buffer = readFileSync(dbPath);
|
|
1936
|
-
const db = new SQL.Database(buffer);
|
|
1937
|
-
try {
|
|
1938
|
-
const results = db.exec("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'");
|
|
1939
|
-
if (results.length === 0)
|
|
1940
|
-
return 0;
|
|
1941
|
-
let count = 0;
|
|
1942
|
-
for (const [, value] of results[0].values) {
|
|
1943
|
-
try {
|
|
1944
|
-
const parsed = JSON.parse(value);
|
|
1945
|
-
const headers = parsed.fullConversationHeadersOnly;
|
|
1946
|
-
if (!Array.isArray(headers) || headers.length < 2)
|
|
1947
|
-
continue;
|
|
1948
|
-
const hasUser = headers.some((h) => h.type === 1);
|
|
1949
|
-
const hasAssistant = headers.some((h) => h.type === 2);
|
|
1950
|
-
if (hasUser && hasAssistant)
|
|
1951
|
-
count++;
|
|
1952
|
-
} catch {
|
|
1953
|
-
continue;
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
return count;
|
|
1957
|
-
} finally {
|
|
1958
|
-
db.close();
|
|
1959
|
-
}
|
|
1960
|
-
} catch {
|
|
1961
|
-
return 0;
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
async function detect() {
|
|
1965
|
-
const home = homedir();
|
|
1966
|
-
const os = platform();
|
|
1967
|
-
const tools = [];
|
|
1968
|
-
const claudeDir = join(home, ".claude", "projects");
|
|
1969
|
-
if (existsSync(claudeDir)) {
|
|
1970
|
-
const sessionCount = countJsonlFiles(claudeDir);
|
|
1971
|
-
tools.push({ name: "Claude Code", id: "claude", available: sessionCount > 0, path: claudeDir, sessionCount });
|
|
1972
|
-
} else {
|
|
1973
|
-
tools.push({ name: "Claude Code", id: "claude", available: false, path: claudeDir, sessionCount: 0 });
|
|
1974
|
-
}
|
|
1975
|
-
const codexConfig = join(home, ".codex", "config.toml");
|
|
1976
|
-
const codexHistory = join(home, ".codex", "history.jsonl");
|
|
1977
|
-
const codexSessions = join(home, ".codex", "sessions");
|
|
1978
|
-
if (existsSync(codexConfig) || existsSync(codexHistory) || existsSync(codexSessions)) {
|
|
1979
|
-
let sessionCount = 0;
|
|
1980
|
-
if (existsSync(codexHistory)) {
|
|
1981
|
-
try {
|
|
1982
|
-
const lines = readFileSync(codexHistory, "utf-8").split(`
|
|
1983
|
-
`).filter(Boolean);
|
|
1984
|
-
const sessions = new Set(lines.map((l) => {
|
|
1985
|
-
try {
|
|
1986
|
-
return JSON.parse(l).session_id;
|
|
1987
|
-
} catch {
|
|
1988
|
-
return null;
|
|
1989
|
-
}
|
|
1990
|
-
}));
|
|
1991
|
-
sessions.delete(null);
|
|
1992
|
-
sessionCount = sessions.size;
|
|
1993
|
-
} catch {}
|
|
1994
|
-
}
|
|
1995
|
-
tools.push({ name: "Codex", id: "codex", available: true, path: codexConfig, sessionCount });
|
|
1996
|
-
} else {
|
|
1997
|
-
tools.push({ name: "Codex", id: "codex", available: false, path: codexConfig, sessionCount: 0 });
|
|
1998
|
-
}
|
|
1999
|
-
let cursorDbPath;
|
|
2000
|
-
if (os === "darwin") {
|
|
2001
|
-
cursorDbPath = join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
2002
|
-
} else if (os === "win32") {
|
|
2003
|
-
cursorDbPath = join(process.env.APPDATA || join(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
2004
|
-
} else {
|
|
2005
|
-
cursorDbPath = join(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
2006
|
-
}
|
|
2007
|
-
tools.push({
|
|
2008
|
-
name: "Cursor",
|
|
2009
|
-
id: "cursor",
|
|
2010
|
-
available: existsSync(cursorDbPath),
|
|
2011
|
-
path: cursorDbPath,
|
|
2012
|
-
sessionCount: existsSync(cursorDbPath) ? await countCursorSessions(cursorDbPath) : 0
|
|
2013
|
-
});
|
|
2014
|
-
const windsurfDir = join(home, ".codeium", "windsurf");
|
|
2015
|
-
tools.push({
|
|
2016
|
-
name: "Windsurf",
|
|
2017
|
-
id: "windsurf",
|
|
2018
|
-
available: existsSync(windsurfDir),
|
|
2019
|
-
path: join(windsurfDir, "mcp_config.json"),
|
|
2020
|
-
sessionCount: 0
|
|
2021
|
-
});
|
|
2022
|
-
let vscodePath;
|
|
2023
|
-
if (os === "darwin") {
|
|
2024
|
-
vscodePath = join(home, "Library", "Application Support", "Code");
|
|
2025
|
-
} else if (os === "win32") {
|
|
2026
|
-
vscodePath = join(process.env.APPDATA || join(home, "AppData", "Roaming"), "Code");
|
|
2027
|
-
} else {
|
|
2028
|
-
vscodePath = join(home, ".config", "Code");
|
|
2029
|
-
}
|
|
2030
|
-
tools.push({
|
|
2031
|
-
name: "VS Code Copilot",
|
|
2032
|
-
id: "vscode",
|
|
2033
|
-
available: existsSync(vscodePath),
|
|
2034
|
-
path: join(vscodePath, "User", "mcp.json"),
|
|
2035
|
-
sessionCount: 0
|
|
2036
|
-
});
|
|
2037
|
-
let clinePath;
|
|
2038
|
-
if (os === "darwin") {
|
|
2039
|
-
clinePath = join(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
2040
|
-
} else if (os === "win32") {
|
|
2041
|
-
clinePath = join(process.env.APPDATA || join(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
2042
|
-
} else {
|
|
2043
|
-
clinePath = join(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
2044
|
-
}
|
|
2045
|
-
tools.push({
|
|
2046
|
-
name: "Cline",
|
|
2047
|
-
id: "cline",
|
|
2048
|
-
available: existsSync(clinePath),
|
|
2049
|
-
path: join(clinePath, "settings", "cline_mcp_settings.json"),
|
|
2050
|
-
sessionCount: 0
|
|
2051
|
-
});
|
|
2052
|
-
const zedPath = os === "darwin" ? join(home, ".zed") : join(home, ".config", "zed");
|
|
2053
|
-
tools.push({
|
|
2054
|
-
name: "Zed",
|
|
2055
|
-
id: "zed",
|
|
2056
|
-
available: existsSync(zedPath),
|
|
2057
|
-
path: join(zedPath, "settings.json"),
|
|
2058
|
-
sessionCount: 0
|
|
2059
|
-
});
|
|
2060
|
-
const openclawDir = join(home, ".openclaw");
|
|
2061
|
-
tools.push({
|
|
2062
|
-
name: "OpenClaw",
|
|
2063
|
-
id: "openclaw",
|
|
2064
|
-
available: existsSync(openclawDir),
|
|
2065
|
-
path: join(openclawDir, "openclaw.json"),
|
|
2066
|
-
sessionCount: 0
|
|
2067
|
-
});
|
|
2068
|
-
const antigravityDir = join(home, ".gemini", "antigravity");
|
|
2069
|
-
const antigravityConvDir = join(antigravityDir, "conversations");
|
|
2070
|
-
let antigravityCount = 0;
|
|
2071
|
-
if (existsSync(antigravityConvDir)) {
|
|
2072
|
-
try {
|
|
2073
|
-
antigravityCount = readdirSync(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
|
|
2074
|
-
} catch {}
|
|
2075
|
-
}
|
|
2076
|
-
tools.push({
|
|
2077
|
-
name: "Antigravity",
|
|
2078
|
-
id: "antigravity",
|
|
2079
|
-
available: existsSync(antigravityDir),
|
|
2080
|
-
path: join(antigravityDir, "mcp_config.json"),
|
|
2081
|
-
sessionCount: antigravityCount
|
|
2082
|
-
});
|
|
2083
|
-
return tools;
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
// src/auth.ts
|
|
2087
|
-
import { execSync } from "node:child_process";
|
|
2088
|
-
import { platform as platform2 } from "node:os";
|
|
2089
|
-
var API_URL = "https://conare.ai";
|
|
2090
|
-
async function browserAuth() {
|
|
2091
|
-
const stateBytes = new Uint8Array(16);
|
|
2092
|
-
crypto.getRandomValues(stateBytes);
|
|
2093
|
-
const state = Array.from(stateBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2094
|
-
const sessionRes = await fetch(`${API_URL}/api/auth/cli-session`, {
|
|
2095
|
-
method: "POST",
|
|
2096
|
-
headers: { "Content-Type": "application/json" },
|
|
2097
|
-
body: JSON.stringify({ state })
|
|
2098
|
-
});
|
|
2099
|
-
if (!sessionRes.ok) {
|
|
2100
|
-
throw new Error(`Failed to create auth session: HTTP ${sessionRes.status}`);
|
|
2101
|
-
}
|
|
2102
|
-
const { code, expiresAt } = await sessionRes.json();
|
|
2103
|
-
const authUrl = `${API_URL}/cli-auth?code=${code}&state=${state}`;
|
|
2104
|
-
const opened = openBrowser(authUrl);
|
|
2105
|
-
if (!opened) {
|
|
2106
|
-
console.log("");
|
|
2107
|
-
console.log(" Open this URL in your browser to sign in:");
|
|
2108
|
-
console.log("");
|
|
2109
|
-
console.log(` ${authUrl}`);
|
|
2110
|
-
console.log("");
|
|
2111
|
-
}
|
|
2112
|
-
const timeout = Math.max(expiresAt - Date.now(), 0);
|
|
2113
|
-
const deadline = Date.now() + timeout;
|
|
2114
|
-
while (Date.now() < deadline) {
|
|
2115
|
-
await sleep(2000);
|
|
2116
|
-
const exchangeRes = await fetch(`${API_URL}/api/auth/cli-exchange`, {
|
|
2117
|
-
method: "POST",
|
|
2118
|
-
headers: { "Content-Type": "application/json" },
|
|
2119
|
-
body: JSON.stringify({ code, state })
|
|
2120
|
-
});
|
|
2121
|
-
if (exchangeRes.status === 202) {
|
|
2122
|
-
continue;
|
|
2123
|
-
}
|
|
2124
|
-
if (exchangeRes.ok) {
|
|
2125
|
-
const data = await exchangeRes.json();
|
|
2126
|
-
return data.apiKey;
|
|
2127
|
-
}
|
|
2128
|
-
if (exchangeRes.status === 410) {
|
|
2129
|
-
throw new Error("Authentication code was already used. Please try again.");
|
|
2130
|
-
}
|
|
2131
|
-
if (exchangeRes.status === 404) {
|
|
2132
|
-
throw new Error("Authentication session expired. Please try again.");
|
|
2133
|
-
}
|
|
2134
|
-
throw new Error(`Authentication failed: HTTP ${exchangeRes.status}`);
|
|
2135
|
-
}
|
|
2136
|
-
throw new Error("Authentication timed out. Please try again.");
|
|
2137
|
-
}
|
|
2138
|
-
function openBrowser(url) {
|
|
2139
|
-
try {
|
|
2140
|
-
const os = platform2();
|
|
2141
|
-
if (os === "darwin") {
|
|
2142
|
-
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
2143
|
-
} else if (os === "win32") {
|
|
2144
|
-
execSync(`start "" "${url}"`, { stdio: "ignore" });
|
|
2145
|
-
} else {
|
|
2146
|
-
try {
|
|
2147
|
-
execSync(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
2148
|
-
} catch {
|
|
2149
|
-
try {
|
|
2150
|
-
execSync(`wslview "${url}"`, { stdio: "ignore" });
|
|
2151
|
-
} catch {
|
|
2152
|
-
return false;
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
}
|
|
2156
|
-
return true;
|
|
2157
|
-
} catch {
|
|
2158
|
-
return false;
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
function sleep(ms) {
|
|
2162
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2163
|
-
}
|
|
1913
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3 } from "node:fs";
|
|
1914
|
+
import { join as join5 } from "node:path";
|
|
1915
|
+
import { homedir as homedir5, platform as platform3 } from "node:os";
|
|
2164
1916
|
|
|
2165
1917
|
// src/ingest/claude.ts
|
|
2166
1918
|
init_shared();
|
|
2167
|
-
import { readdirSync
|
|
2168
|
-
import { join as
|
|
2169
|
-
import { homedir as
|
|
1919
|
+
import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
1920
|
+
import { join as join2, basename } from "node:path";
|
|
1921
|
+
import { homedir as homedir2, platform } from "node:os";
|
|
2170
1922
|
var MIN_TURN_LEN = 50;
|
|
2171
1923
|
function resolveProjectName(dirName) {
|
|
2172
1924
|
const segments = dirName.replace(/^-/, "").split("-");
|
|
2173
|
-
const isWindows =
|
|
1925
|
+
const isWindows = platform() === "win32";
|
|
2174
1926
|
let resolved;
|
|
2175
1927
|
let startIdx;
|
|
2176
1928
|
if (isWindows && segments.length > 0 && /^[A-Za-z]$/.test(segments[0])) {
|
|
@@ -2185,8 +1937,8 @@ function resolveProjectName(dirName) {
|
|
|
2185
1937
|
let found = false;
|
|
2186
1938
|
for (let end = segments.length;end > i; end--) {
|
|
2187
1939
|
const candidate = segments.slice(i, end).join("-");
|
|
2188
|
-
const candidatePath =
|
|
2189
|
-
if (
|
|
1940
|
+
const candidatePath = join2(resolved, candidate);
|
|
1941
|
+
if (existsSync2(candidatePath)) {
|
|
2190
1942
|
resolved = candidatePath;
|
|
2191
1943
|
i = end;
|
|
2192
1944
|
found = true;
|
|
@@ -2194,11 +1946,11 @@ function resolveProjectName(dirName) {
|
|
|
2194
1946
|
}
|
|
2195
1947
|
}
|
|
2196
1948
|
if (!found) {
|
|
2197
|
-
resolved =
|
|
1949
|
+
resolved = join2(resolved, segments[i]);
|
|
2198
1950
|
i++;
|
|
2199
1951
|
}
|
|
2200
1952
|
}
|
|
2201
|
-
const home =
|
|
1953
|
+
const home = homedir2();
|
|
2202
1954
|
const sep = isWindows ? "\\" : "/";
|
|
2203
1955
|
if (resolved.startsWith(home + sep)) {
|
|
2204
1956
|
return resolved.slice(home.length + 1).replace(/\\/g, "/");
|
|
@@ -2282,30 +2034,65 @@ function getParentUuid(lines) {
|
|
|
2282
2034
|
}
|
|
2283
2035
|
return;
|
|
2284
2036
|
}
|
|
2037
|
+
function countImportableClaudeSessions() {
|
|
2038
|
+
const projectsDir = join2(homedir2(), ".claude", "projects");
|
|
2039
|
+
let count = 0;
|
|
2040
|
+
let projectDirs;
|
|
2041
|
+
try {
|
|
2042
|
+
projectDirs = readdirSync(projectsDir);
|
|
2043
|
+
} catch {
|
|
2044
|
+
return 0;
|
|
2045
|
+
}
|
|
2046
|
+
for (const projDir of projectDirs) {
|
|
2047
|
+
const projPath = join2(projectsDir, projDir);
|
|
2048
|
+
let files;
|
|
2049
|
+
try {
|
|
2050
|
+
files = readdirSync(projPath).filter((f) => f.endsWith(".jsonl"));
|
|
2051
|
+
} catch {
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
2054
|
+
for (const file of files) {
|
|
2055
|
+
try {
|
|
2056
|
+
const raw = readFileSync2(join2(projPath, file), "utf-8");
|
|
2057
|
+
const lines = raw.split(`
|
|
2058
|
+
`);
|
|
2059
|
+
const parentUuid = getParentUuid(lines);
|
|
2060
|
+
if (parentUuid != null)
|
|
2061
|
+
continue;
|
|
2062
|
+
const { turns } = parseSession(lines);
|
|
2063
|
+
if (turns.length > 0)
|
|
2064
|
+
count++;
|
|
2065
|
+
} catch {
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
return count;
|
|
2071
|
+
}
|
|
2285
2072
|
function ingestClaude() {
|
|
2286
|
-
const projectsDir =
|
|
2073
|
+
const projectsDir = join2(homedir2(), ".claude", "projects");
|
|
2287
2074
|
const memories = [];
|
|
2288
2075
|
const sessionIds = [];
|
|
2289
2076
|
let filtered = 0;
|
|
2290
2077
|
let deduped = 0;
|
|
2291
2078
|
let projectDirs;
|
|
2292
2079
|
try {
|
|
2293
|
-
projectDirs =
|
|
2080
|
+
projectDirs = readdirSync(projectsDir);
|
|
2294
2081
|
} catch {
|
|
2295
2082
|
return { memories, sessionIds, skipped: 0, filtered, deduped };
|
|
2296
2083
|
}
|
|
2297
2084
|
for (const projDir of projectDirs) {
|
|
2298
|
-
const projPath =
|
|
2085
|
+
const projPath = join2(projectsDir, projDir);
|
|
2299
2086
|
const project = resolveProjectName(projDir);
|
|
2300
2087
|
let files;
|
|
2301
2088
|
try {
|
|
2302
|
-
files =
|
|
2089
|
+
files = readdirSync(projPath).filter((f) => f.endsWith(".jsonl"));
|
|
2303
2090
|
} catch {
|
|
2304
2091
|
continue;
|
|
2305
2092
|
}
|
|
2306
2093
|
for (const file of files) {
|
|
2307
2094
|
const sessionId = basename(file, ".jsonl");
|
|
2308
|
-
const raw =
|
|
2095
|
+
const raw = readFileSync2(join2(projPath, file), "utf-8");
|
|
2309
2096
|
const lines = raw.split(`
|
|
2310
2097
|
`);
|
|
2311
2098
|
const parentUuid = getParentUuid(lines);
|
|
@@ -2351,9 +2138,9 @@ function ingestClaude() {
|
|
|
2351
2138
|
|
|
2352
2139
|
// src/ingest/codex.ts
|
|
2353
2140
|
init_shared();
|
|
2354
|
-
import { existsSync as
|
|
2355
|
-
import { join as
|
|
2356
|
-
import { homedir as
|
|
2141
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
2142
|
+
import { join as join3, basename as basename2 } from "node:path";
|
|
2143
|
+
import { homedir as homedir3 } from "node:os";
|
|
2357
2144
|
function isCodexBoilerplate(text) {
|
|
2358
2145
|
return text.startsWith("# AGENTS.md instructions for") || text.startsWith("<INSTRUCTIONS>") || text.startsWith("<user_instructions>") || text.startsWith("<user_action>");
|
|
2359
2146
|
}
|
|
@@ -2362,7 +2149,7 @@ function extractCwd(text) {
|
|
|
2362
2149
|
return match ? match[1] : null;
|
|
2363
2150
|
}
|
|
2364
2151
|
function projectFromCwd(cwd) {
|
|
2365
|
-
const home =
|
|
2152
|
+
const home = homedir3();
|
|
2366
2153
|
const normalized = cwd.replace(/\\/g, "/");
|
|
2367
2154
|
const normalizedHome = home.replace(/\\/g, "/");
|
|
2368
2155
|
if (normalized.startsWith(normalizedHome + "/")) {
|
|
@@ -2375,8 +2162,8 @@ function ingestCodex() {
|
|
|
2375
2162
|
const sessionIds = [];
|
|
2376
2163
|
let filtered = 0;
|
|
2377
2164
|
let deduped = 0;
|
|
2378
|
-
const sessionsDir =
|
|
2379
|
-
if (
|
|
2165
|
+
const sessionsDir = join3(homedir3(), ".codex", "sessions");
|
|
2166
|
+
if (existsSync3(sessionsDir)) {
|
|
2380
2167
|
try {
|
|
2381
2168
|
const stats = { filtered: 0, deduped: 0 };
|
|
2382
2169
|
walkCodexSessions(sessionsDir, memories, sessionIds, stats);
|
|
@@ -2386,87 +2173,119 @@ function ingestCodex() {
|
|
|
2386
2173
|
}
|
|
2387
2174
|
return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
|
|
2388
2175
|
}
|
|
2176
|
+
function parseCodexSession(lines) {
|
|
2177
|
+
let date = null;
|
|
2178
|
+
let startedAt = null;
|
|
2179
|
+
let updatedAt = null;
|
|
2180
|
+
let sourceTimestamp = null;
|
|
2181
|
+
let project = null;
|
|
2182
|
+
const rounds = [];
|
|
2183
|
+
let currentUser = null;
|
|
2184
|
+
let currentAssistant = [];
|
|
2185
|
+
for (const line of lines) {
|
|
2186
|
+
try {
|
|
2187
|
+
const obj = JSON.parse(line);
|
|
2188
|
+
if (typeof obj.timestamp === "string") {
|
|
2189
|
+
if (!startedAt)
|
|
2190
|
+
startedAt = obj.timestamp;
|
|
2191
|
+
updatedAt = obj.timestamp;
|
|
2192
|
+
const parsed = parseTimestampMs(obj.timestamp);
|
|
2193
|
+
if (parsed !== null)
|
|
2194
|
+
sourceTimestamp = parsed;
|
|
2195
|
+
date = obj.timestamp.slice(0, 10);
|
|
2196
|
+
}
|
|
2197
|
+
if (obj.type === "session_meta" && obj.payload?.cwd) {
|
|
2198
|
+
project = projectFromCwd(obj.payload.cwd);
|
|
2199
|
+
}
|
|
2200
|
+
let role;
|
|
2201
|
+
let msgContent;
|
|
2202
|
+
if (obj.type === "response_item" && obj.payload?.type === "message") {
|
|
2203
|
+
role = obj.payload.role;
|
|
2204
|
+
msgContent = Array.isArray(obj.payload.content) ? obj.payload.content : undefined;
|
|
2205
|
+
} else if (obj.type === "message" && obj.role) {
|
|
2206
|
+
role = obj.role;
|
|
2207
|
+
msgContent = Array.isArray(obj.content) ? obj.content : undefined;
|
|
2208
|
+
}
|
|
2209
|
+
if (!role || !msgContent)
|
|
2210
|
+
continue;
|
|
2211
|
+
if (role === "user") {
|
|
2212
|
+
for (const block of msgContent) {
|
|
2213
|
+
if (block.type === "input_text" && block.text) {
|
|
2214
|
+
if (!project) {
|
|
2215
|
+
const cwd = extractCwd(block.text);
|
|
2216
|
+
if (cwd)
|
|
2217
|
+
project = projectFromCwd(cwd);
|
|
2218
|
+
}
|
|
2219
|
+
const text = cleanText(block.text);
|
|
2220
|
+
if (isCodexBoilerplate(text))
|
|
2221
|
+
continue;
|
|
2222
|
+
if (text.length >= 50) {
|
|
2223
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
2224
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
2225
|
+
}
|
|
2226
|
+
currentUser = text;
|
|
2227
|
+
currentAssistant = [];
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
} else if (role === "assistant") {
|
|
2232
|
+
for (const block of msgContent) {
|
|
2233
|
+
if (block.type === "output_text" && block.text) {
|
|
2234
|
+
const text = cleanText(block.text);
|
|
2235
|
+
if (!isNarration(text)) {
|
|
2236
|
+
currentAssistant.push(text);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
} catch {
|
|
2242
|
+
continue;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
if (currentUser !== null && currentAssistant.length > 0) {
|
|
2246
|
+
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
2247
|
+
}
|
|
2248
|
+
return { rounds, date, startedAt, updatedAt, sourceTimestamp, project };
|
|
2249
|
+
}
|
|
2250
|
+
function walkCodexSessionFiles(dir, visit) {
|
|
2251
|
+
try {
|
|
2252
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
2253
|
+
const fullPath = join3(dir, entry.name);
|
|
2254
|
+
if (entry.isDirectory()) {
|
|
2255
|
+
walkCodexSessionFiles(fullPath, visit);
|
|
2256
|
+
} else if (entry.name.endsWith(".jsonl")) {
|
|
2257
|
+
visit(fullPath);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
} catch {}
|
|
2261
|
+
}
|
|
2262
|
+
function countImportableCodexSessions() {
|
|
2263
|
+
const sessionsDir = join3(homedir3(), ".codex", "sessions");
|
|
2264
|
+
if (!existsSync3(sessionsDir))
|
|
2265
|
+
return 0;
|
|
2266
|
+
let count = 0;
|
|
2267
|
+
walkCodexSessionFiles(sessionsDir, (filePath) => {
|
|
2268
|
+
try {
|
|
2269
|
+
const lines = readFileSync3(filePath, "utf-8").split(`
|
|
2270
|
+
`).filter(Boolean);
|
|
2271
|
+
const { rounds } = parseCodexSession(lines);
|
|
2272
|
+
if (rounds.length > 0)
|
|
2273
|
+
count++;
|
|
2274
|
+
} catch {}
|
|
2275
|
+
});
|
|
2276
|
+
return count;
|
|
2277
|
+
}
|
|
2389
2278
|
function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
2390
2279
|
try {
|
|
2391
|
-
for (const entry of
|
|
2280
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
2392
2281
|
if (entry.isDirectory()) {
|
|
2393
|
-
walkCodexSessions(
|
|
2282
|
+
walkCodexSessions(join3(dir, entry.name), memories, sessionIds, stats);
|
|
2394
2283
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
2395
2284
|
const sessionId = basename2(entry.name, ".jsonl");
|
|
2396
2285
|
try {
|
|
2397
|
-
const lines =
|
|
2286
|
+
const lines = readFileSync3(join3(dir, entry.name), "utf-8").split(`
|
|
2398
2287
|
`).filter(Boolean);
|
|
2399
|
-
|
|
2400
|
-
let startedAt = null;
|
|
2401
|
-
let updatedAt = null;
|
|
2402
|
-
let sourceTimestamp = null;
|
|
2403
|
-
let project = null;
|
|
2404
|
-
const rounds = [];
|
|
2405
|
-
let currentUser = null;
|
|
2406
|
-
let currentAssistant = [];
|
|
2407
|
-
for (const line of lines) {
|
|
2408
|
-
try {
|
|
2409
|
-
const obj = JSON.parse(line);
|
|
2410
|
-
if (typeof obj.timestamp === "string") {
|
|
2411
|
-
if (!startedAt)
|
|
2412
|
-
startedAt = obj.timestamp;
|
|
2413
|
-
updatedAt = obj.timestamp;
|
|
2414
|
-
const parsed = parseTimestampMs(obj.timestamp);
|
|
2415
|
-
if (parsed !== null)
|
|
2416
|
-
sourceTimestamp = parsed;
|
|
2417
|
-
date = obj.timestamp.slice(0, 10);
|
|
2418
|
-
}
|
|
2419
|
-
if (obj.type === "session_meta" && obj.payload?.cwd) {
|
|
2420
|
-
project = projectFromCwd(obj.payload.cwd);
|
|
2421
|
-
}
|
|
2422
|
-
let role;
|
|
2423
|
-
let msgContent;
|
|
2424
|
-
if (obj.type === "response_item" && obj.payload?.type === "message") {
|
|
2425
|
-
role = obj.payload.role;
|
|
2426
|
-
msgContent = Array.isArray(obj.payload.content) ? obj.payload.content : undefined;
|
|
2427
|
-
} else if (obj.type === "message" && obj.role) {
|
|
2428
|
-
role = obj.role;
|
|
2429
|
-
msgContent = Array.isArray(obj.content) ? obj.content : undefined;
|
|
2430
|
-
}
|
|
2431
|
-
if (!role || !msgContent)
|
|
2432
|
-
continue;
|
|
2433
|
-
if (role === "user") {
|
|
2434
|
-
for (const block of msgContent) {
|
|
2435
|
-
if (block.type === "input_text" && block.text) {
|
|
2436
|
-
if (!project) {
|
|
2437
|
-
const cwd = extractCwd(block.text);
|
|
2438
|
-
if (cwd)
|
|
2439
|
-
project = projectFromCwd(cwd);
|
|
2440
|
-
}
|
|
2441
|
-
const text = cleanText(block.text);
|
|
2442
|
-
if (isCodexBoilerplate(text))
|
|
2443
|
-
continue;
|
|
2444
|
-
if (text.length >= 50) {
|
|
2445
|
-
if (currentUser !== null && currentAssistant.length > 0) {
|
|
2446
|
-
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
2447
|
-
}
|
|
2448
|
-
currentUser = text;
|
|
2449
|
-
currentAssistant = [];
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
} else if (role === "assistant") {
|
|
2454
|
-
for (const block of msgContent) {
|
|
2455
|
-
if (block.type === "output_text" && block.text) {
|
|
2456
|
-
const text = cleanText(block.text);
|
|
2457
|
-
if (!isNarration(text)) {
|
|
2458
|
-
currentAssistant.push(text);
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
}
|
|
2463
|
-
} catch {
|
|
2464
|
-
continue;
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
if (currentUser !== null && currentAssistant.length > 0) {
|
|
2468
|
-
rounds.push({ user: currentUser, assistantParts: currentAssistant });
|
|
2469
|
-
}
|
|
2288
|
+
const { rounds, date, startedAt, updatedAt, sourceTimestamp, project } = parseCodexSession(lines);
|
|
2470
2289
|
if (rounds.length === 0) {
|
|
2471
2290
|
stats.filtered++;
|
|
2472
2291
|
continue;
|
|
@@ -2511,9 +2330,10 @@ function walkCodexSessions(dir, memories, sessionIds, stats) {
|
|
|
2511
2330
|
|
|
2512
2331
|
// src/ingest/cursor.ts
|
|
2513
2332
|
init_shared();
|
|
2514
|
-
import { readFileSync as
|
|
2515
|
-
import { join as
|
|
2516
|
-
import { createRequire as
|
|
2333
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
2334
|
+
import { dirname, join as join4 } from "node:path";
|
|
2335
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2336
|
+
import { homedir as homedir4 } from "node:os";
|
|
2517
2337
|
var MAX_DB_SIZE = 2 * 1024 * 1024 * 1024;
|
|
2518
2338
|
var WARN_DB_SIZE = 500 * 1024 * 1024;
|
|
2519
2339
|
var MIN_TURN_LEN2 = 50;
|
|
@@ -2526,26 +2346,88 @@ function parseCursorTimestamp(value) {
|
|
|
2526
2346
|
function loadSqlJs(wasmDir) {
|
|
2527
2347
|
try {
|
|
2528
2348
|
if (wasmDir) {
|
|
2529
|
-
const require2 =
|
|
2349
|
+
const require2 = createRequire2(join4(wasmDir, "sql.js", "package.json"));
|
|
2530
2350
|
return require2("sql.js");
|
|
2531
2351
|
} else {
|
|
2532
|
-
const require2 =
|
|
2352
|
+
const require2 = createRequire2(import.meta.url);
|
|
2533
2353
|
return require2("sql.js");
|
|
2534
2354
|
}
|
|
2535
2355
|
} catch {
|
|
2536
2356
|
return null;
|
|
2537
2357
|
}
|
|
2538
2358
|
}
|
|
2359
|
+
function resolveSqlJsWasmDir(wasmDir) {
|
|
2360
|
+
if (wasmDir)
|
|
2361
|
+
return wasmDir;
|
|
2362
|
+
try {
|
|
2363
|
+
const require2 = createRequire2(import.meta.url);
|
|
2364
|
+
const packageJson = require2.resolve("sql.js/package.json");
|
|
2365
|
+
return dirname(dirname(packageJson));
|
|
2366
|
+
} catch {
|
|
2367
|
+
const installedBundleDir = join4(homedir4(), ".conare", "bin", "node_modules");
|
|
2368
|
+
return existsSync4(join4(installedBundleDir, "sql.js", "package.json")) ? installedBundleDir : undefined;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2539
2371
|
function openDb(initSqlJs, dbPath, wasmDir) {
|
|
2540
2372
|
const locateOpts = {};
|
|
2541
2373
|
if (wasmDir) {
|
|
2542
|
-
locateOpts.locateFile = (file) =>
|
|
2374
|
+
locateOpts.locateFile = (file) => join4(wasmDir, "sql.js", "dist", file);
|
|
2543
2375
|
}
|
|
2544
2376
|
return initSqlJs(locateOpts).then((SQL) => {
|
|
2545
|
-
const buffer =
|
|
2377
|
+
const buffer = readFileSync4(dbPath);
|
|
2546
2378
|
return new SQL.Database(buffer);
|
|
2547
2379
|
});
|
|
2548
2380
|
}
|
|
2381
|
+
async function countImportableCursorSessions(dbPath, wasmDir) {
|
|
2382
|
+
try {
|
|
2383
|
+
const fileSize = statSync(dbPath).size;
|
|
2384
|
+
if (fileSize > MAX_DB_SIZE)
|
|
2385
|
+
return 0;
|
|
2386
|
+
} catch {
|
|
2387
|
+
return 0;
|
|
2388
|
+
}
|
|
2389
|
+
const effectiveWasmDir = resolveSqlJsWasmDir(wasmDir);
|
|
2390
|
+
const initSqlJs = loadSqlJs(effectiveWasmDir);
|
|
2391
|
+
if (!initSqlJs)
|
|
2392
|
+
return 0;
|
|
2393
|
+
let db;
|
|
2394
|
+
try {
|
|
2395
|
+
db = await openDb(initSqlJs, dbPath, effectiveWasmDir);
|
|
2396
|
+
} catch {
|
|
2397
|
+
return 0;
|
|
2398
|
+
}
|
|
2399
|
+
try {
|
|
2400
|
+
let rows = [];
|
|
2401
|
+
try {
|
|
2402
|
+
const results = db.exec("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'");
|
|
2403
|
+
if (results.length > 0)
|
|
2404
|
+
rows = results[0].values;
|
|
2405
|
+
} catch {
|
|
2406
|
+
return 0;
|
|
2407
|
+
}
|
|
2408
|
+
let count = 0;
|
|
2409
|
+
for (const [key, value] of rows) {
|
|
2410
|
+
const composerId = key.replace("composerData:", "");
|
|
2411
|
+
let parsed;
|
|
2412
|
+
try {
|
|
2413
|
+
parsed = JSON.parse(value);
|
|
2414
|
+
if (!parsed || typeof parsed !== "object")
|
|
2415
|
+
continue;
|
|
2416
|
+
} catch {
|
|
2417
|
+
continue;
|
|
2418
|
+
}
|
|
2419
|
+
const bubbleHeaders = parsed.fullConversationHeadersOnly;
|
|
2420
|
+
if (!Array.isArray(bubbleHeaders) || bubbleHeaders.length === 0)
|
|
2421
|
+
continue;
|
|
2422
|
+
const turns = extractTurns(db, composerId, bubbleHeaders);
|
|
2423
|
+
if (turns.length > 0)
|
|
2424
|
+
count++;
|
|
2425
|
+
}
|
|
2426
|
+
return count;
|
|
2427
|
+
} finally {
|
|
2428
|
+
db.close();
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2549
2431
|
function extractTurns(db, composerId, bubbleHeaders) {
|
|
2550
2432
|
const turns = [];
|
|
2551
2433
|
let pendingUser = null;
|
|
@@ -2590,14 +2472,15 @@ async function ingestCursor(dbPath, wasmDir) {
|
|
|
2590
2472
|
if (fileSize > WARN_DB_SIZE) {
|
|
2591
2473
|
console.log(` Warning: large database (${(fileSize / 1024 / 1024).toFixed(0)}MB), loading into memory...`);
|
|
2592
2474
|
}
|
|
2593
|
-
const
|
|
2475
|
+
const effectiveWasmDir = resolveSqlJsWasmDir(wasmDir);
|
|
2476
|
+
const initSqlJs = loadSqlJs(effectiveWasmDir);
|
|
2594
2477
|
if (!initSqlJs) {
|
|
2595
2478
|
console.log(" Skipping Cursor: sql.js not available");
|
|
2596
2479
|
return { memories, sessionIds, skipped: 0, filtered, deduped };
|
|
2597
2480
|
}
|
|
2598
2481
|
let db;
|
|
2599
2482
|
try {
|
|
2600
|
-
db = await openDb(initSqlJs, dbPath,
|
|
2483
|
+
db = await openDb(initSqlJs, dbPath, effectiveWasmDir);
|
|
2601
2484
|
} catch (e) {
|
|
2602
2485
|
console.log(` Skipping Cursor: cannot open database (${e.message})`);
|
|
2603
2486
|
return { memories, sessionIds, skipped: 0, filtered, deduped };
|
|
@@ -2671,15 +2554,202 @@ async function ingestCursor(dbPath, wasmDir) {
|
|
|
2671
2554
|
return { memories, sessionIds, skipped: filtered + deduped, filtered, deduped };
|
|
2672
2555
|
}
|
|
2673
2556
|
|
|
2557
|
+
// src/detect.ts
|
|
2558
|
+
async function detect() {
|
|
2559
|
+
const home = homedir5();
|
|
2560
|
+
const os = platform3();
|
|
2561
|
+
const tools = [];
|
|
2562
|
+
const claudeDir = join5(home, ".claude", "projects");
|
|
2563
|
+
if (existsSync5(claudeDir)) {
|
|
2564
|
+
const sessionCount = countImportableClaudeSessions();
|
|
2565
|
+
tools.push({ name: "Claude Code", id: "claude", available: sessionCount > 0, path: claudeDir, sessionCount, sessionCountApproximate: true });
|
|
2566
|
+
} else {
|
|
2567
|
+
tools.push({ name: "Claude Code", id: "claude", available: false, path: claudeDir, sessionCount: 0 });
|
|
2568
|
+
}
|
|
2569
|
+
const codexConfig = join5(home, ".codex", "config.toml");
|
|
2570
|
+
const codexSessions = join5(home, ".codex", "sessions");
|
|
2571
|
+
if (existsSync5(codexConfig) || existsSync5(codexSessions)) {
|
|
2572
|
+
const sessionCount = countImportableCodexSessions();
|
|
2573
|
+
tools.push({ name: "Codex", id: "codex", available: true, path: codexConfig, sessionCount, sessionCountApproximate: true });
|
|
2574
|
+
} else {
|
|
2575
|
+
tools.push({ name: "Codex", id: "codex", available: false, path: codexConfig, sessionCount: 0 });
|
|
2576
|
+
}
|
|
2577
|
+
let cursorDbPath;
|
|
2578
|
+
if (os === "darwin") {
|
|
2579
|
+
cursorDbPath = join5(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
2580
|
+
} else if (os === "win32") {
|
|
2581
|
+
cursorDbPath = join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
2582
|
+
} else {
|
|
2583
|
+
cursorDbPath = join5(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
2584
|
+
}
|
|
2585
|
+
tools.push({
|
|
2586
|
+
name: "Cursor",
|
|
2587
|
+
id: "cursor",
|
|
2588
|
+
available: existsSync5(cursorDbPath),
|
|
2589
|
+
path: cursorDbPath,
|
|
2590
|
+
sessionCount: existsSync5(cursorDbPath) ? await countImportableCursorSessions(cursorDbPath) : 0,
|
|
2591
|
+
sessionCountApproximate: true
|
|
2592
|
+
});
|
|
2593
|
+
const windsurfDir = join5(home, ".codeium", "windsurf");
|
|
2594
|
+
tools.push({
|
|
2595
|
+
name: "Windsurf",
|
|
2596
|
+
id: "windsurf",
|
|
2597
|
+
available: existsSync5(windsurfDir),
|
|
2598
|
+
path: join5(windsurfDir, "mcp_config.json"),
|
|
2599
|
+
sessionCount: 0
|
|
2600
|
+
});
|
|
2601
|
+
let vscodePath;
|
|
2602
|
+
if (os === "darwin") {
|
|
2603
|
+
vscodePath = join5(home, "Library", "Application Support", "Code");
|
|
2604
|
+
} else if (os === "win32") {
|
|
2605
|
+
vscodePath = join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "Code");
|
|
2606
|
+
} else {
|
|
2607
|
+
vscodePath = join5(home, ".config", "Code");
|
|
2608
|
+
}
|
|
2609
|
+
tools.push({
|
|
2610
|
+
name: "VS Code Copilot",
|
|
2611
|
+
id: "vscode",
|
|
2612
|
+
available: existsSync5(vscodePath),
|
|
2613
|
+
path: join5(vscodePath, "User", "mcp.json"),
|
|
2614
|
+
sessionCount: 0
|
|
2615
|
+
});
|
|
2616
|
+
let clinePath;
|
|
2617
|
+
if (os === "darwin") {
|
|
2618
|
+
clinePath = join5(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
2619
|
+
} else if (os === "win32") {
|
|
2620
|
+
clinePath = join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
2621
|
+
} else {
|
|
2622
|
+
clinePath = join5(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev");
|
|
2623
|
+
}
|
|
2624
|
+
tools.push({
|
|
2625
|
+
name: "Cline",
|
|
2626
|
+
id: "cline",
|
|
2627
|
+
available: existsSync5(clinePath),
|
|
2628
|
+
path: join5(clinePath, "settings", "cline_mcp_settings.json"),
|
|
2629
|
+
sessionCount: 0
|
|
2630
|
+
});
|
|
2631
|
+
const zedPath = os === "darwin" ? join5(home, ".zed") : join5(home, ".config", "zed");
|
|
2632
|
+
tools.push({
|
|
2633
|
+
name: "Zed",
|
|
2634
|
+
id: "zed",
|
|
2635
|
+
available: existsSync5(zedPath),
|
|
2636
|
+
path: join5(zedPath, "settings.json"),
|
|
2637
|
+
sessionCount: 0
|
|
2638
|
+
});
|
|
2639
|
+
const openclawDir = join5(home, ".openclaw");
|
|
2640
|
+
tools.push({
|
|
2641
|
+
name: "OpenClaw",
|
|
2642
|
+
id: "openclaw",
|
|
2643
|
+
available: existsSync5(openclawDir),
|
|
2644
|
+
path: join5(openclawDir, "openclaw.json"),
|
|
2645
|
+
sessionCount: 0
|
|
2646
|
+
});
|
|
2647
|
+
const antigravityDir = join5(home, ".gemini", "antigravity");
|
|
2648
|
+
const antigravityConvDir = join5(antigravityDir, "conversations");
|
|
2649
|
+
let antigravityCount = 0;
|
|
2650
|
+
if (existsSync5(antigravityConvDir)) {
|
|
2651
|
+
try {
|
|
2652
|
+
antigravityCount = readdirSync3(antigravityConvDir).filter((f) => f.endsWith(".pb")).length;
|
|
2653
|
+
} catch {}
|
|
2654
|
+
}
|
|
2655
|
+
tools.push({
|
|
2656
|
+
name: "Antigravity",
|
|
2657
|
+
id: "antigravity",
|
|
2658
|
+
available: existsSync5(antigravityDir),
|
|
2659
|
+
path: join5(antigravityDir, "mcp_config.json"),
|
|
2660
|
+
sessionCount: antigravityCount
|
|
2661
|
+
});
|
|
2662
|
+
return tools;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
// src/auth.ts
|
|
2666
|
+
import { execSync } from "node:child_process";
|
|
2667
|
+
import { platform as platform4 } from "node:os";
|
|
2668
|
+
var API_URL = "https://conare.ai";
|
|
2669
|
+
async function browserAuth() {
|
|
2670
|
+
const stateBytes = new Uint8Array(16);
|
|
2671
|
+
crypto.getRandomValues(stateBytes);
|
|
2672
|
+
const state = Array.from(stateBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2673
|
+
const sessionRes = await fetch(`${API_URL}/api/auth/cli-session`, {
|
|
2674
|
+
method: "POST",
|
|
2675
|
+
headers: { "Content-Type": "application/json" },
|
|
2676
|
+
body: JSON.stringify({ state })
|
|
2677
|
+
});
|
|
2678
|
+
if (!sessionRes.ok) {
|
|
2679
|
+
throw new Error(`Failed to create auth session: HTTP ${sessionRes.status}`);
|
|
2680
|
+
}
|
|
2681
|
+
const { code, expiresAt } = await sessionRes.json();
|
|
2682
|
+
const authUrl = `${API_URL}/cli-auth?code=${code}&state=${state}`;
|
|
2683
|
+
const opened = openBrowser(authUrl);
|
|
2684
|
+
if (!opened) {
|
|
2685
|
+
console.log("");
|
|
2686
|
+
console.log(" Open this URL in your browser to sign in:");
|
|
2687
|
+
console.log("");
|
|
2688
|
+
console.log(` ${authUrl}`);
|
|
2689
|
+
console.log("");
|
|
2690
|
+
}
|
|
2691
|
+
const timeout = Math.max(expiresAt - Date.now(), 0);
|
|
2692
|
+
const deadline = Date.now() + timeout;
|
|
2693
|
+
while (Date.now() < deadline) {
|
|
2694
|
+
await sleep(2000);
|
|
2695
|
+
const exchangeRes = await fetch(`${API_URL}/api/auth/cli-exchange`, {
|
|
2696
|
+
method: "POST",
|
|
2697
|
+
headers: { "Content-Type": "application/json" },
|
|
2698
|
+
body: JSON.stringify({ code, state })
|
|
2699
|
+
});
|
|
2700
|
+
if (exchangeRes.status === 202) {
|
|
2701
|
+
continue;
|
|
2702
|
+
}
|
|
2703
|
+
if (exchangeRes.ok) {
|
|
2704
|
+
const data = await exchangeRes.json();
|
|
2705
|
+
return data.apiKey;
|
|
2706
|
+
}
|
|
2707
|
+
if (exchangeRes.status === 410) {
|
|
2708
|
+
throw new Error("Authentication code was already used. Please try again.");
|
|
2709
|
+
}
|
|
2710
|
+
if (exchangeRes.status === 404) {
|
|
2711
|
+
throw new Error("Authentication session expired. Please try again.");
|
|
2712
|
+
}
|
|
2713
|
+
throw new Error(`Authentication failed: HTTP ${exchangeRes.status}`);
|
|
2714
|
+
}
|
|
2715
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
2716
|
+
}
|
|
2717
|
+
function openBrowser(url) {
|
|
2718
|
+
try {
|
|
2719
|
+
const os = platform4();
|
|
2720
|
+
if (os === "darwin") {
|
|
2721
|
+
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
2722
|
+
} else if (os === "win32") {
|
|
2723
|
+
execSync(`start "" "${url}"`, { stdio: "ignore" });
|
|
2724
|
+
} else {
|
|
2725
|
+
try {
|
|
2726
|
+
execSync(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
2727
|
+
} catch {
|
|
2728
|
+
try {
|
|
2729
|
+
execSync(`wslview "${url}"`, { stdio: "ignore" });
|
|
2730
|
+
} catch {
|
|
2731
|
+
return false;
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
return true;
|
|
2736
|
+
} catch {
|
|
2737
|
+
return false;
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
function sleep(ms) {
|
|
2741
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2674
2744
|
// src/index.ts
|
|
2675
2745
|
init_codebase();
|
|
2676
2746
|
init_shared();
|
|
2677
2747
|
init_api();
|
|
2678
2748
|
|
|
2679
2749
|
// src/configure.ts
|
|
2680
|
-
import { existsSync as
|
|
2681
|
-
import { dirname, join as join7 } from "node:path";
|
|
2682
|
-
import { homedir as
|
|
2750
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2, symlinkSync, readlinkSync, rmSync } from "node:fs";
|
|
2751
|
+
import { dirname as dirname2, join as join7 } from "node:path";
|
|
2752
|
+
import { homedir as homedir6, platform as platform5 } from "node:os";
|
|
2683
2753
|
import { spawnSync } from "node:child_process";
|
|
2684
2754
|
var CONARE_URL = "https://conare.ai";
|
|
2685
2755
|
var SERVER_NAME = "conare";
|
|
@@ -2696,13 +2766,13 @@ var MCP_TARGETS = [
|
|
|
2696
2766
|
];
|
|
2697
2767
|
function readJsonFile(path) {
|
|
2698
2768
|
try {
|
|
2699
|
-
return JSON.parse(
|
|
2769
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
2700
2770
|
} catch {
|
|
2701
2771
|
return {};
|
|
2702
2772
|
}
|
|
2703
2773
|
}
|
|
2704
2774
|
function writeJsonFile(path, data) {
|
|
2705
|
-
mkdirSync2(
|
|
2775
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
2706
2776
|
writeFileSync2(path, JSON.stringify(data, null, 2) + `
|
|
2707
2777
|
`);
|
|
2708
2778
|
}
|
|
@@ -2740,7 +2810,7 @@ function configureClaude(apiKey) {
|
|
|
2740
2810
|
}).status === 0) {
|
|
2741
2811
|
return "\x1B[32m✓\x1B[0m Claude Code";
|
|
2742
2812
|
}
|
|
2743
|
-
const claudeConfigPath = join7(
|
|
2813
|
+
const claudeConfigPath = join7(homedir6(), ".claude.json");
|
|
2744
2814
|
const config = readJsonFile(claudeConfigPath);
|
|
2745
2815
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2746
2816
|
config.mcpServers = {};
|
|
@@ -2750,7 +2820,7 @@ function configureClaude(apiKey) {
|
|
|
2750
2820
|
return "\x1B[32m✓\x1B[0m Claude Code (json fallback)";
|
|
2751
2821
|
}
|
|
2752
2822
|
function configureCodex(apiKey) {
|
|
2753
|
-
const configPath = join7(
|
|
2823
|
+
const configPath = join7(homedir6(), ".codex", "config.toml");
|
|
2754
2824
|
if (spawnSync("codex", ["mcp", "add", SERVER_NAME, "--url", `${CONARE_URL}/mcp`, "--header", `Authorization: Bearer ${apiKey}`], {
|
|
2755
2825
|
stdio: "ignore",
|
|
2756
2826
|
shell: platform5() === "win32"
|
|
@@ -2759,7 +2829,7 @@ function configureCodex(apiKey) {
|
|
|
2759
2829
|
}
|
|
2760
2830
|
let toml = "";
|
|
2761
2831
|
try {
|
|
2762
|
-
toml =
|
|
2832
|
+
toml = readFileSync6(configPath, "utf-8");
|
|
2763
2833
|
} catch {}
|
|
2764
2834
|
const sectionHeader = `[mcp_servers.${SERVER_NAME}]`;
|
|
2765
2835
|
const newSection = [
|
|
@@ -2780,11 +2850,11 @@ function configureCodex(apiKey) {
|
|
|
2780
2850
|
${newSection}
|
|
2781
2851
|
` : `${newSection}
|
|
2782
2852
|
`;
|
|
2783
|
-
mkdirSync2(
|
|
2853
|
+
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
2784
2854
|
writeFileSync2(configPath, result);
|
|
2785
|
-
const oldMcpJson = join7(
|
|
2855
|
+
const oldMcpJson = join7(homedir6(), ".codex", "mcp.json");
|
|
2786
2856
|
try {
|
|
2787
|
-
if (
|
|
2857
|
+
if (existsSync7(oldMcpJson)) {
|
|
2788
2858
|
const old = readJsonFile(oldMcpJson);
|
|
2789
2859
|
const servers = old.mcpServers;
|
|
2790
2860
|
if (servers && (("conare" in servers) || ("conare-memory" in servers))) {
|
|
@@ -2801,7 +2871,7 @@ ${newSection}
|
|
|
2801
2871
|
return "\x1B[32m✓\x1B[0m Codex";
|
|
2802
2872
|
}
|
|
2803
2873
|
function configureCursor(apiKey) {
|
|
2804
|
-
const configPath = join7(
|
|
2874
|
+
const configPath = join7(homedir6(), ".cursor", "mcp.json");
|
|
2805
2875
|
const config = readJsonFile(configPath);
|
|
2806
2876
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2807
2877
|
config.mcpServers = {};
|
|
@@ -2814,7 +2884,7 @@ function configureCursor(apiKey) {
|
|
|
2814
2884
|
return "\x1B[32m✓\x1B[0m Cursor";
|
|
2815
2885
|
}
|
|
2816
2886
|
function configureWindsurf(apiKey) {
|
|
2817
|
-
const configPath = join7(
|
|
2887
|
+
const configPath = join7(homedir6(), ".codeium", "windsurf", "mcp_config.json");
|
|
2818
2888
|
const config = readJsonFile(configPath);
|
|
2819
2889
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2820
2890
|
config.mcpServers = {};
|
|
@@ -2830,11 +2900,11 @@ function configureVscode(apiKey) {
|
|
|
2830
2900
|
const os = platform5();
|
|
2831
2901
|
let configPath;
|
|
2832
2902
|
if (os === "darwin") {
|
|
2833
|
-
configPath = join7(
|
|
2903
|
+
configPath = join7(homedir6(), "Library", "Application Support", "Code", "User", "mcp.json");
|
|
2834
2904
|
} else if (os === "win32") {
|
|
2835
|
-
configPath = join7(process.env.APPDATA || join7(
|
|
2905
|
+
configPath = join7(process.env.APPDATA || join7(homedir6(), "AppData", "Roaming"), "Code", "User", "mcp.json");
|
|
2836
2906
|
} else {
|
|
2837
|
-
configPath = join7(
|
|
2907
|
+
configPath = join7(homedir6(), ".config", "Code", "User", "mcp.json");
|
|
2838
2908
|
}
|
|
2839
2909
|
const config = readJsonFile(configPath);
|
|
2840
2910
|
if (!config.servers || typeof config.servers !== "object") {
|
|
@@ -2852,11 +2922,11 @@ function configureCline(apiKey) {
|
|
|
2852
2922
|
const os = platform5();
|
|
2853
2923
|
let configPath;
|
|
2854
2924
|
if (os === "darwin") {
|
|
2855
|
-
configPath = join7(
|
|
2925
|
+
configPath = join7(homedir6(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
2856
2926
|
} else if (os === "win32") {
|
|
2857
|
-
configPath = join7(process.env.APPDATA || join7(
|
|
2927
|
+
configPath = join7(process.env.APPDATA || join7(homedir6(), "AppData", "Roaming"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
2858
2928
|
} else {
|
|
2859
|
-
configPath = join7(
|
|
2929
|
+
configPath = join7(homedir6(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
2860
2930
|
}
|
|
2861
2931
|
const config = readJsonFile(configPath);
|
|
2862
2932
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
@@ -2874,9 +2944,9 @@ function configureZed(apiKey) {
|
|
|
2874
2944
|
const os = platform5();
|
|
2875
2945
|
let configPath;
|
|
2876
2946
|
if (os === "darwin") {
|
|
2877
|
-
configPath = join7(
|
|
2947
|
+
configPath = join7(homedir6(), ".zed", "settings.json");
|
|
2878
2948
|
} else {
|
|
2879
|
-
configPath = join7(
|
|
2949
|
+
configPath = join7(homedir6(), ".config", "zed", "settings.json");
|
|
2880
2950
|
}
|
|
2881
2951
|
const config = readJsonFile(configPath);
|
|
2882
2952
|
if (!config.context_servers || typeof config.context_servers !== "object") {
|
|
@@ -2897,7 +2967,7 @@ function configureZed(apiKey) {
|
|
|
2897
2967
|
return "\x1B[32m✓\x1B[0m Zed";
|
|
2898
2968
|
}
|
|
2899
2969
|
function configureOpenclaw(apiKey) {
|
|
2900
|
-
const configPath = join7(
|
|
2970
|
+
const configPath = join7(homedir6(), ".openclaw", "openclaw.json");
|
|
2901
2971
|
const config = readJsonFile(configPath);
|
|
2902
2972
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2903
2973
|
config.mcpServers = {};
|
|
@@ -2911,7 +2981,7 @@ function configureOpenclaw(apiKey) {
|
|
|
2911
2981
|
return "\x1B[32m✓\x1B[0m OpenClaw";
|
|
2912
2982
|
}
|
|
2913
2983
|
function configureAntigravity(apiKey) {
|
|
2914
|
-
const configPath = join7(
|
|
2984
|
+
const configPath = join7(homedir6(), ".gemini", "antigravity", "mcp_config.json");
|
|
2915
2985
|
const config = readJsonFile(configPath);
|
|
2916
2986
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2917
2987
|
config.mcpServers = {};
|
|
@@ -3042,14 +3112,14 @@ The wizard handles everything: account creation, API key, MCP configuration, bac
|
|
|
3042
3112
|
For manual setup, visit [conare.ai](https://conare.ai).
|
|
3043
3113
|
`;
|
|
3044
3114
|
function installSkill() {
|
|
3045
|
-
const skillDir = join7(
|
|
3115
|
+
const skillDir = join7(homedir6(), ".agents", "skills", "conare");
|
|
3046
3116
|
mkdirSync2(skillDir, { recursive: true });
|
|
3047
3117
|
writeFileSync2(join7(skillDir, "SKILL.md"), SKILL_MD);
|
|
3048
|
-
const claudeSkillsDir = join7(
|
|
3118
|
+
const claudeSkillsDir = join7(homedir6(), ".claude", "skills");
|
|
3049
3119
|
const claudeSkillDir = join7(claudeSkillsDir, "conare");
|
|
3050
3120
|
try {
|
|
3051
|
-
if (
|
|
3052
|
-
if (
|
|
3121
|
+
if (existsSync7(claudeSkillsDir)) {
|
|
3122
|
+
if (existsSync7(claudeSkillDir)) {
|
|
3053
3123
|
try {
|
|
3054
3124
|
if (readlinkSync(claudeSkillDir) === skillDir)
|
|
3055
3125
|
return "\x1B[32m✓\x1B[0m Agent Skill";
|
|
@@ -3080,16 +3150,16 @@ function configureMcp(apiKey, targets = ["claude", "codex"]) {
|
|
|
3080
3150
|
}
|
|
3081
3151
|
|
|
3082
3152
|
// src/config.ts
|
|
3083
|
-
import { existsSync as
|
|
3153
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3084
3154
|
import { join as join8 } from "node:path";
|
|
3085
|
-
import { homedir as
|
|
3086
|
-
var CONFIG_DIR = join8(
|
|
3155
|
+
import { homedir as homedir7 } from "node:os";
|
|
3156
|
+
var CONFIG_DIR = join8(homedir7(), ".conare");
|
|
3087
3157
|
var CONFIG_PATH = join8(CONFIG_DIR, "config.json");
|
|
3088
3158
|
function readConfig() {
|
|
3089
3159
|
try {
|
|
3090
|
-
if (!
|
|
3160
|
+
if (!existsSync8(CONFIG_PATH))
|
|
3091
3161
|
return {};
|
|
3092
|
-
return JSON.parse(
|
|
3162
|
+
return JSON.parse(readFileSync7(CONFIG_PATH, "utf-8"));
|
|
3093
3163
|
} catch {
|
|
3094
3164
|
return {};
|
|
3095
3165
|
}
|
|
@@ -3109,16 +3179,16 @@ function getSavedApiKey() {
|
|
|
3109
3179
|
}
|
|
3110
3180
|
|
|
3111
3181
|
// src/sync.ts
|
|
3112
|
-
import { existsSync as
|
|
3113
|
-
import { join as join9, dirname as
|
|
3114
|
-
import { homedir as
|
|
3182
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, unlinkSync, readFileSync as readFileSync8, chmodSync, cpSync, rmSync as rmSync2, symlinkSync as symlinkSync2, readlinkSync as readlinkSync2, appendFileSync } from "node:fs";
|
|
3183
|
+
import { join as join9, dirname as dirname3 } from "node:path";
|
|
3184
|
+
import { homedir as homedir8, platform as platform6 } from "node:os";
|
|
3115
3185
|
import { execSync as execSync3 } from "node:child_process";
|
|
3116
|
-
var CONARE_DIR = join9(
|
|
3186
|
+
var CONARE_DIR = join9(homedir8(), ".conare");
|
|
3117
3187
|
var BIN_DIR = join9(CONARE_DIR, "bin");
|
|
3118
3188
|
var CONFIG_PATH2 = join9(CONARE_DIR, "config.json");
|
|
3119
3189
|
var PLIST_LABEL = "ai.conare.ingest";
|
|
3120
|
-
var PLIST_PATH = join9(
|
|
3121
|
-
var SYSTEMD_DIR = join9(
|
|
3190
|
+
var PLIST_PATH = join9(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
3191
|
+
var SYSTEMD_DIR = join9(homedir8(), ".config", "systemd", "user");
|
|
3122
3192
|
var SYSTEMD_SERVICE = join9(SYSTEMD_DIR, "conare-sync.service");
|
|
3123
3193
|
var SYSTEMD_TIMER = join9(SYSTEMD_DIR, "conare-sync.timer");
|
|
3124
3194
|
var TASK_NAME = "ConareMemorySync";
|
|
@@ -3263,7 +3333,7 @@ function persistBinary(apiKey) {
|
|
|
3263
3333
|
throw new Error("Could not locate CLI bundle. Run from an installed conare package.");
|
|
3264
3334
|
}
|
|
3265
3335
|
const dest = join9(BIN_DIR, "conare-ingest.mjs");
|
|
3266
|
-
const content =
|
|
3336
|
+
const content = readFileSync8(cliEntry, "utf-8");
|
|
3267
3337
|
writeFileSync4(dest, content);
|
|
3268
3338
|
const sqlJsDir = findSqlJs();
|
|
3269
3339
|
if (sqlJsDir) {
|
|
@@ -3286,17 +3356,17 @@ function persistBinary(apiKey) {
|
|
|
3286
3356
|
`, { mode: 384 });
|
|
3287
3357
|
}
|
|
3288
3358
|
function isValidJsBundle(path) {
|
|
3289
|
-
if (!
|
|
3359
|
+
if (!existsSync9(path))
|
|
3290
3360
|
return false;
|
|
3291
3361
|
if (path.endsWith(".ts") || path.endsWith(".tsx"))
|
|
3292
3362
|
return false;
|
|
3293
|
-
const head =
|
|
3363
|
+
const head = readFileSync8(path, "utf-8").slice(0, 2000);
|
|
3294
3364
|
if (/\btype\s+\{/.test(head) || /,\s*type\s+\w+/.test(head))
|
|
3295
3365
|
return false;
|
|
3296
3366
|
return true;
|
|
3297
3367
|
}
|
|
3298
3368
|
function findCliBundle() {
|
|
3299
|
-
const dir =
|
|
3369
|
+
const dir = dirname3(new URL(import.meta.url).pathname);
|
|
3300
3370
|
const distCandidates = [
|
|
3301
3371
|
join9(dir, "index.js"),
|
|
3302
3372
|
join9(dir, "..", "dist", "index.js")
|
|
@@ -3313,11 +3383,11 @@ function findCliBundle() {
|
|
|
3313
3383
|
function findSqlJs() {
|
|
3314
3384
|
const candidates = [
|
|
3315
3385
|
join9(process.cwd(), "node_modules", "sql.js"),
|
|
3316
|
-
join9(
|
|
3317
|
-
join9(
|
|
3386
|
+
join9(dirname3(new URL(import.meta.url).pathname), "..", "node_modules", "sql.js"),
|
|
3387
|
+
join9(dirname3(new URL(import.meta.url).pathname), "..", "..", "node_modules", "sql.js")
|
|
3318
3388
|
];
|
|
3319
3389
|
for (const c of candidates) {
|
|
3320
|
-
if (
|
|
3390
|
+
if (existsSync9(c))
|
|
3321
3391
|
return c;
|
|
3322
3392
|
}
|
|
3323
3393
|
return null;
|
|
@@ -3326,7 +3396,7 @@ function cleanupOldLaunchAgent() {
|
|
|
3326
3396
|
try {
|
|
3327
3397
|
execSync3(`launchctl bootout gui/${uid()} "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
3328
3398
|
} catch {}
|
|
3329
|
-
if (
|
|
3399
|
+
if (existsSync9(PLIST_PATH)) {
|
|
3330
3400
|
unlinkSync(PLIST_PATH);
|
|
3331
3401
|
}
|
|
3332
3402
|
}
|
|
@@ -3355,7 +3425,7 @@ function clampCronInterval(intervalMinutes) {
|
|
|
3355
3425
|
}
|
|
3356
3426
|
function setupCron(intervalMinutes) {
|
|
3357
3427
|
const clamped = clampCronInterval(intervalMinutes);
|
|
3358
|
-
const cronCmd = `"${
|
|
3428
|
+
const cronCmd = `"${homedir8()}/.conare/bin/run.sh"`;
|
|
3359
3429
|
const cronLine = `*/${clamped} * * * * ${cronCmd} # conare-sync`;
|
|
3360
3430
|
try {
|
|
3361
3431
|
const existing = execSync3("crontab -l 2>/dev/null", { encoding: "utf-8" });
|
|
@@ -3398,13 +3468,13 @@ function runSyncNow() {
|
|
|
3398
3468
|
try {
|
|
3399
3469
|
if (os === "win32") {
|
|
3400
3470
|
const runCmd = join9(BIN_DIR, "run.cmd");
|
|
3401
|
-
if (
|
|
3471
|
+
if (existsSync9(runCmd)) {
|
|
3402
3472
|
execSync3(`"${runCmd}"`, { stdio: "ignore", timeout: 60000 });
|
|
3403
3473
|
return true;
|
|
3404
3474
|
}
|
|
3405
3475
|
} else {
|
|
3406
3476
|
const runSh = join9(BIN_DIR, "run.sh");
|
|
3407
|
-
if (
|
|
3477
|
+
if (existsSync9(runSh)) {
|
|
3408
3478
|
execSync3(`/bin/bash "${runSh}"`, { stdio: "ignore", timeout: 60000 });
|
|
3409
3479
|
return true;
|
|
3410
3480
|
}
|
|
@@ -3458,7 +3528,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
|
|
|
3458
3528
|
chmodSync(wrapper, 493);
|
|
3459
3529
|
const symlinkTarget = "/usr/local/bin/conare";
|
|
3460
3530
|
try {
|
|
3461
|
-
if (
|
|
3531
|
+
if (existsSync9(symlinkTarget)) {
|
|
3462
3532
|
try {
|
|
3463
3533
|
const existing = readlinkSync2(symlinkTarget);
|
|
3464
3534
|
if (existing === wrapper)
|
|
@@ -3476,7 +3546,7 @@ exec "$NODE" "$CONARE_DIR/bin/conare-ingest.mjs" "$@"
|
|
|
3476
3546
|
const shellProfile = getShellProfile();
|
|
3477
3547
|
if (shellProfile) {
|
|
3478
3548
|
try {
|
|
3479
|
-
const profileContent =
|
|
3549
|
+
const profileContent = existsSync9(shellProfile) ? readFileSync8(shellProfile, "utf-8") : "";
|
|
3480
3550
|
const exportLine = `export PATH="$HOME/.conare/bin:$PATH"`;
|
|
3481
3551
|
if (!profileContent.includes(".conare/bin")) {
|
|
3482
3552
|
appendFileSync(shellProfile, `
|
|
@@ -3494,19 +3564,19 @@ ${exportLine}
|
|
|
3494
3564
|
}
|
|
3495
3565
|
}
|
|
3496
3566
|
function getShellProfile() {
|
|
3497
|
-
const home =
|
|
3567
|
+
const home = homedir8();
|
|
3498
3568
|
const shell = process.env.SHELL || "";
|
|
3499
3569
|
if (shell.includes("zsh"))
|
|
3500
3570
|
return join9(home, ".zshrc");
|
|
3501
3571
|
if (shell.includes("bash")) {
|
|
3502
3572
|
const profile = join9(home, ".bash_profile");
|
|
3503
|
-
if (platform6() === "darwin" &&
|
|
3573
|
+
if (platform6() === "darwin" && existsSync9(profile))
|
|
3504
3574
|
return profile;
|
|
3505
3575
|
return join9(home, ".bashrc");
|
|
3506
3576
|
}
|
|
3507
|
-
if (
|
|
3577
|
+
if (existsSync9(join9(home, ".zshrc")))
|
|
3508
3578
|
return join9(home, ".zshrc");
|
|
3509
|
-
if (
|
|
3579
|
+
if (existsSync9(join9(home, ".bashrc")))
|
|
3510
3580
|
return join9(home, ".bashrc");
|
|
3511
3581
|
return null;
|
|
3512
3582
|
}
|
|
@@ -3576,9 +3646,9 @@ function uninstallSync() {
|
|
|
3576
3646
|
try {
|
|
3577
3647
|
execSync3("systemctl --user disable --now conare-sync.timer 2>/dev/null", { stdio: "ignore" });
|
|
3578
3648
|
} catch {}
|
|
3579
|
-
if (
|
|
3649
|
+
if (existsSync9(SYSTEMD_SERVICE))
|
|
3580
3650
|
unlinkSync(SYSTEMD_SERVICE);
|
|
3581
|
-
if (
|
|
3651
|
+
if (existsSync9(SYSTEMD_TIMER))
|
|
3582
3652
|
unlinkSync(SYSTEMD_TIMER);
|
|
3583
3653
|
try {
|
|
3584
3654
|
execSync3("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
@@ -3589,15 +3659,15 @@ function uninstallSync() {
|
|
|
3589
3659
|
}
|
|
3590
3660
|
}
|
|
3591
3661
|
const configPath = join9(CONARE_DIR, "config.json");
|
|
3592
|
-
if (
|
|
3662
|
+
if (existsSync9(configPath))
|
|
3593
3663
|
unlinkSync(configPath);
|
|
3594
3664
|
const lockDir = join9(CONARE_DIR, "sync.lock.d");
|
|
3595
|
-
if (
|
|
3665
|
+
if (existsSync9(lockDir))
|
|
3596
3666
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
3597
3667
|
const lockFile = join9(CONARE_DIR, "sync.lock");
|
|
3598
|
-
if (
|
|
3668
|
+
if (existsSync9(lockFile))
|
|
3599
3669
|
unlinkSync(lockFile);
|
|
3600
|
-
if (
|
|
3670
|
+
if (existsSync9(BIN_DIR)) {
|
|
3601
3671
|
rmSync2(BIN_DIR, { recursive: true, force: true });
|
|
3602
3672
|
messages.push("Removed ~/.conare/bin/");
|
|
3603
3673
|
}
|
|
@@ -3677,6 +3747,9 @@ function renderSourceBreakdown(memories) {
|
|
|
3677
3747
|
}
|
|
3678
3748
|
return [...counts.entries()].map(([tag, count]) => `${CHAT_CONTAINER_LABELS[tag] || tag}: ${count}`).join(", ");
|
|
3679
3749
|
}
|
|
3750
|
+
function formatSessionCount(count, approximate) {
|
|
3751
|
+
return `${approximate ? "~" : ""}${count} sessions`;
|
|
3752
|
+
}
|
|
3680
3753
|
function parseArgs() {
|
|
3681
3754
|
const args = process.argv.slice(2);
|
|
3682
3755
|
let key = "";
|
|
@@ -3829,12 +3902,12 @@ async function runInstall() {
|
|
|
3829
3902
|
console.log("");
|
|
3830
3903
|
}
|
|
3831
3904
|
async function runLogout() {
|
|
3832
|
-
const { unlinkSync: unlinkSync2, existsSync:
|
|
3905
|
+
const { unlinkSync: unlinkSync2, existsSync: existsSync11 } = await import("node:fs");
|
|
3833
3906
|
const { join: join11 } = await import("node:path");
|
|
3834
|
-
const { homedir:
|
|
3907
|
+
const { homedir: homedir9 } = await import("node:os");
|
|
3835
3908
|
const messages = uninstallSync();
|
|
3836
|
-
const manifestPath = join11(
|
|
3837
|
-
if (
|
|
3909
|
+
const manifestPath = join11(homedir9(), ".conare", "ingested.json");
|
|
3910
|
+
if (existsSync11(manifestPath)) {
|
|
3838
3911
|
unlinkSync2(manifestPath);
|
|
3839
3912
|
messages.push("Removed local index history");
|
|
3840
3913
|
}
|
|
@@ -3856,8 +3929,8 @@ async function main() {
|
|
|
3856
3929
|
let configFileKey;
|
|
3857
3930
|
if (opts.configFile) {
|
|
3858
3931
|
try {
|
|
3859
|
-
const { readFileSync:
|
|
3860
|
-
const raw = JSON.parse(
|
|
3932
|
+
const { readFileSync: readFileSync9 } = await import("node:fs");
|
|
3933
|
+
const raw = JSON.parse(readFileSync9(opts.configFile, "utf-8"));
|
|
3861
3934
|
configFileKey = raw.apiKey || raw.key;
|
|
3862
3935
|
if (!configFileKey) {
|
|
3863
3936
|
console.error(`Error: no apiKey/key found in ${opts.configFile}`);
|
|
@@ -3884,7 +3957,8 @@ async function main() {
|
|
|
3884
3957
|
label: target.label,
|
|
3885
3958
|
available: true,
|
|
3886
3959
|
recommended: target.defaultSelected,
|
|
3887
|
-
detectedCount: undefined
|
|
3960
|
+
detectedCount: undefined,
|
|
3961
|
+
detectedCountApproximate: undefined
|
|
3888
3962
|
}));
|
|
3889
3963
|
let interactiveMode = false;
|
|
3890
3964
|
if (shouldRunInteractive) {
|
|
@@ -3896,7 +3970,8 @@ async function main() {
|
|
|
3896
3970
|
label: target.label,
|
|
3897
3971
|
available: detected?.available,
|
|
3898
3972
|
recommended: target.defaultSelected && detected?.available !== false,
|
|
3899
|
-
detectedCount: detected?.sessionCount
|
|
3973
|
+
detectedCount: detected?.sessionCount,
|
|
3974
|
+
detectedCountApproximate: detected?.sessionCountApproximate
|
|
3900
3975
|
};
|
|
3901
3976
|
});
|
|
3902
3977
|
startSetup();
|
|
@@ -3976,7 +4051,7 @@ async function main() {
|
|
|
3976
4051
|
log("");
|
|
3977
4052
|
}
|
|
3978
4053
|
}
|
|
3979
|
-
if (!opts.wasmDir &&
|
|
4054
|
+
if (!opts.wasmDir && existsSync10(join10(process.cwd(), "node_modules", "sql.js"))) {
|
|
3980
4055
|
opts.wasmDir = join10(process.cwd(), "node_modules");
|
|
3981
4056
|
}
|
|
3982
4057
|
if (effectiveConfigOnly) {
|
|
@@ -4099,7 +4174,7 @@ Nothing new to index.`);
|
|
|
4099
4174
|
log("Detected AI tools:");
|
|
4100
4175
|
for (const t of tools) {
|
|
4101
4176
|
if (t.available) {
|
|
4102
|
-
log(` + ${t.name}${t.sessionCount || t.sessionCount === 0 ? ` — ${t.sessionCount}
|
|
4177
|
+
log(` + ${t.name}${t.sessionCount || t.sessionCount === 0 ? ` — ${formatSessionCount(t.sessionCount, t.sessionCountApproximate)}` : ""}`);
|
|
4103
4178
|
} else {
|
|
4104
4179
|
log(` - ${t.name} (not found)`);
|
|
4105
4180
|
}
|