@vibe-cafe/vibe-usage 0.10.19 → 0.10.21
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/README.md +33 -6
- package/package.json +1 -1
- package/src/daemon-service.js +347 -7
- package/src/extra-roots.js +312 -0
- package/src/index.js +70 -3
- package/src/init.js +6 -2
- package/src/parsers/antigravity-db.js +9 -8
- package/src/parsers/antigravity.js +88 -11
- package/src/parsers/codex.js +62 -11
- package/src/parsers/cursor.js +21 -2
- package/src/parsers/grok.js +102 -18
- package/src/parsers/index.js +2 -0
- package/src/parsers/mcode.js +182 -0
- package/src/parsers/pi-coding-agent.js +17 -2
- package/src/parsers/pi-session-jsonl.js +23 -2
- package/src/pi-roots.js +43 -11
- package/src/sync.js +14 -3
- package/src/tools.js +46 -12
package/src/pi-roots.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
-
import { delimiter, join } from 'node:path';
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { delimiter, isAbsolute, join } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
+
import { piSessionsDir } from './extra-roots.js';
|
|
4
5
|
|
|
5
6
|
function expandHome(value) {
|
|
6
7
|
const trimmed = value.trim();
|
|
@@ -41,18 +42,49 @@ export function looksLikeOmpAgentDir(agentDir) {
|
|
|
41
42
|
|| existsSync(join(agentDir, 'agent.db'));
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
// Pi resolves a session directory from `--session-dir`, then
|
|
46
|
+
// PI_CODING_AGENT_SESSION_DIR, then `sessionDir` in settings.json. Only the
|
|
47
|
+
// last two are discoverable after the fact, and both name the sessions
|
|
48
|
+
// directory itself (no `sessions` segment is appended).
|
|
49
|
+
function settingsSessionDir(agentDir) {
|
|
50
|
+
try {
|
|
51
|
+
const raw = readFileSync(join(agentDir, 'settings.json'), 'utf-8');
|
|
52
|
+
const value = JSON.parse(raw)?.sessionDir;
|
|
53
|
+
if (typeof value !== 'string') return null;
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
// Pi also accepts project-relative values. Those resolve against a cwd we
|
|
56
|
+
// do not have here, so only absolute and ~-anchored paths are scanned.
|
|
57
|
+
if (!trimmed || !(isAbsolute(trimmed) || trimmed.startsWith('~'))) return null;
|
|
58
|
+
return expandHome(trimmed);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getPiSessionDirs(extraRoots = []) {
|
|
65
|
+
// Explicitly configured roots are user intent, not a fixture: they are always
|
|
66
|
+
// scanned, and they never replace the default store. A root whose shape no
|
|
67
|
+
// longer resolves drops out here; the parser reports that as skipped.
|
|
68
|
+
const extraDirs = extraRoots.map(piSessionsDir).filter(dir => dir !== null);
|
|
69
|
+
|
|
45
70
|
const override = process.env.VIBE_USAGE_PI_SESSION_DIRS?.trim();
|
|
46
|
-
if (override) return uniqueExistingDirs(override.split(delimiter));
|
|
71
|
+
if (override) return uniqueExistingDirs([...override.split(delimiter), ...extraDirs]);
|
|
47
72
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
73
|
+
const envAgentDir = process.env.PI_CODING_AGENT_DIR?.trim();
|
|
74
|
+
const agentDir = envAgentDir ? expandHome(envAgentDir) : join(homedir(), '.pi', 'agent');
|
|
75
|
+
// OMP inherits PI_CODING_AGENT_DIR from Pi. Do not parse an identifiable
|
|
76
|
+
// OMP store again as source=pi-coding-agent.
|
|
77
|
+
const isOmpStore = Boolean(envAgentDir) && looksLikeOmpAgentDir(agentDir);
|
|
78
|
+
|
|
79
|
+
const dirs = [];
|
|
80
|
+
if (!isOmpStore) {
|
|
81
|
+
dirs.push(join(agentDir, 'sessions'));
|
|
82
|
+
const envSessionDir = process.env.PI_CODING_AGENT_SESSION_DIR?.trim();
|
|
83
|
+
if (envSessionDir) dirs.push(expandHome(envSessionDir));
|
|
84
|
+
const configured = settingsSessionDir(agentDir);
|
|
85
|
+
if (configured) dirs.push(configured);
|
|
54
86
|
}
|
|
55
|
-
return uniqueExistingDirs([
|
|
87
|
+
return uniqueExistingDirs([...dirs, ...extraDirs]);
|
|
56
88
|
}
|
|
57
89
|
|
|
58
90
|
export function getOmpSessionDirs() {
|
package/src/sync.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createSyncClient, forBatch } from './client-meta.js';
|
|
|
9
9
|
import { parsers } from './parsers/index.js';
|
|
10
10
|
import { aggregateToBuckets } from './parsers/aggregate.js';
|
|
11
11
|
import { normalizeParserResult } from './parsers/contract.js';
|
|
12
|
+
import { extraRootList } from './extra-roots.js';
|
|
12
13
|
import { success, failure, warn, arrow, link, dim } from './output.js';
|
|
13
14
|
|
|
14
15
|
const BATCH_SIZE = 100;
|
|
@@ -20,6 +21,12 @@ function formatBytes(bytes) {
|
|
|
20
21
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
/** Hide only Cursor's intentional transient fetch soft-skip in quiet (daemon) syncs. */
|
|
25
|
+
export function shouldSuppressParserWarning(source, message, quiet) {
|
|
26
|
+
if (!quiet) return false;
|
|
27
|
+
return source === 'cursor' && message.startsWith('cursor: Cursor usage export skipped (');
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
export function resolveUploadProjectSetting(settings) {
|
|
24
31
|
if (typeof settings?.uploadProject !== 'boolean') {
|
|
25
32
|
const error = new Error('SETTINGS_UNAVAILABLE');
|
|
@@ -147,9 +154,12 @@ export async function runSync({
|
|
|
147
154
|
PARSER_CONCURRENCY,
|
|
148
155
|
async ([source, parse]) => {
|
|
149
156
|
try {
|
|
150
|
-
const result =
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
const result = await parse({
|
|
158
|
+
extraRoots: extraRootList(config.extraRoots?.[source]),
|
|
159
|
+
...(source === 'codex' ? {
|
|
160
|
+
codexExtraHome: resolveCodexExtraHome(config.codexExtraHome, codexExtraHome),
|
|
161
|
+
} : {}),
|
|
162
|
+
});
|
|
153
163
|
return { source, result };
|
|
154
164
|
} catch (err) {
|
|
155
165
|
return { source, error: err };
|
|
@@ -175,6 +185,7 @@ export async function runSync({
|
|
|
175
185
|
parserProgress.push({ source, ...indexing });
|
|
176
186
|
}
|
|
177
187
|
for (const message of warnings) {
|
|
188
|
+
if (shouldSuppressParserWarning(source, message, quiet)) continue;
|
|
178
189
|
process.stderr.write(`${dim(` ${message}`)}\n`);
|
|
179
190
|
}
|
|
180
191
|
// A parser may deliberately suppress a transient error (Cursor network
|
package/src/tools.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
-
import { isAbsolute, join, posix, resolve, win32 } from 'node:path';
|
|
2
|
+
import { dirname, isAbsolute, join, posix, resolve, win32 } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { findClaudeCodeDataDirs } from './claude-roots.js';
|
|
5
5
|
import { findCindyDataDirs, getCindyDataRoots } from './cindy-roots.js';
|
|
6
6
|
import { codexSessionDirs, resolveCodexHomes } from './codex-roots.js';
|
|
7
|
+
import {
|
|
8
|
+
antigravityConversationDirs,
|
|
9
|
+
discoverCodexHomes,
|
|
10
|
+
extraRootList,
|
|
11
|
+
grokSessionsDir,
|
|
12
|
+
} from './extra-roots.js';
|
|
7
13
|
import { findClineDataDirs } from './cline-roots.js';
|
|
8
14
|
import { findCraftDataDirs } from './craft-roots.js';
|
|
9
15
|
import { findOmpDataDirs, findPiDataDirs } from './pi-roots.js';
|
|
@@ -106,8 +112,9 @@ function findOpenclawDataDirs() {
|
|
|
106
112
|
// Codex keeps live sessions in ~/.codex/sessions and moves completed ones to
|
|
107
113
|
// ~/.codex/archived_sessions. Detect Codex if either dir exists, so a user
|
|
108
114
|
// whose sessions have all been archived is still recognized.
|
|
109
|
-
export function findCodexDataDirs(codexExtraHome) {
|
|
110
|
-
|
|
115
|
+
export function findCodexDataDirs(codexExtraHome, extraRoots = []) {
|
|
116
|
+
const configuredHomes = extraRoots.flatMap(root => discoverCodexHomes(root).homes);
|
|
117
|
+
return [...new Set([...resolveCodexHomes(codexExtraHome), ...configuredHomes])]
|
|
111
118
|
.flatMap(codexSessionDirs)
|
|
112
119
|
.filter(existsSync);
|
|
113
120
|
}
|
|
@@ -143,6 +150,17 @@ export function findDshDataDirs() {
|
|
|
143
150
|
return [getDshSessionsDir()].filter(existsSync);
|
|
144
151
|
}
|
|
145
152
|
|
|
153
|
+
/** mcode runtime database: VIBE_USAGE_MCODE_DB wins, then MCODE_HOME, then ~/.minimax. */
|
|
154
|
+
export function getMcodeDbPath(env = process.env, home = homedir()) {
|
|
155
|
+
const override = env.VIBE_USAGE_MCODE_DB?.trim();
|
|
156
|
+
if (override) return isAbsolute(override) ? override : resolve(override);
|
|
157
|
+
if (env.MCODE_HOME && !isAbsolute(env.MCODE_HOME)) {
|
|
158
|
+
throw new Error(`MCODE_HOME must be an absolute path, got: ${JSON.stringify(env.MCODE_HOME)}`);
|
|
159
|
+
}
|
|
160
|
+
const root = env.MCODE_HOME || join(home, '.minimax');
|
|
161
|
+
return join(root, 'v2', 'sqlite', 'runtime-state.sqlite');
|
|
162
|
+
}
|
|
163
|
+
|
|
146
164
|
export function getMimocodeDbPath(env = process.env) {
|
|
147
165
|
if (env.MIMOCODE_HOME && !isAbsolute(env.MIMOCODE_HOME)) {
|
|
148
166
|
throw new Error(`MIMOCODE_HOME must be an absolute path, got: ${JSON.stringify(env.MIMOCODE_HOME)}`);
|
|
@@ -154,11 +172,12 @@ export function getMimocodeDbPath(env = process.env) {
|
|
|
154
172
|
return isAbsolute(env.MIMOCODE_DB) ? env.MIMOCODE_DB : join(dataDir, env.MIMOCODE_DB);
|
|
155
173
|
}
|
|
156
174
|
|
|
157
|
-
function findAntigravityDataDirs() {
|
|
158
|
-
return [
|
|
175
|
+
export function findAntigravityDataDirs(extraRoots = []) {
|
|
176
|
+
return [...new Set([
|
|
159
177
|
join(homedir(), '.gemini', 'antigravity'),
|
|
160
178
|
join(homedir(), '.gemini', 'antigravity-cli'),
|
|
161
|
-
|
|
179
|
+
...extraRoots.flatMap(root => antigravityConversationDirs(root).map(dirname)),
|
|
180
|
+
])].filter(existsSync);
|
|
162
181
|
}
|
|
163
182
|
|
|
164
183
|
export function findTraeCliDataDirs() {
|
|
@@ -193,10 +212,13 @@ export function getGrokSessionsDir() {
|
|
|
193
212
|
}
|
|
194
213
|
|
|
195
214
|
// Detect Grok when sessions/ exists under GROK_HOME (or the test override).
|
|
196
|
-
export function findGrokDataDirs() {
|
|
215
|
+
export function findGrokDataDirs(extraRoots = []) {
|
|
197
216
|
const testDir = process.env.VIBE_USAGE_GROK_SESSIONS?.trim();
|
|
198
217
|
if (testDir) return [testDir].filter(existsSync);
|
|
199
|
-
return [
|
|
218
|
+
return [...new Set([
|
|
219
|
+
join(getGrokHome(), 'sessions'),
|
|
220
|
+
...extraRoots.map(grokSessionsDir),
|
|
221
|
+
])].filter(existsSync);
|
|
200
222
|
}
|
|
201
223
|
|
|
202
224
|
export function getDimAgentDbPath() {
|
|
@@ -240,13 +262,15 @@ export const TOOLS = [
|
|
|
240
262
|
name: 'Codex CLI',
|
|
241
263
|
id: 'codex',
|
|
242
264
|
dataDir: join(homedir(), '.codex', 'sessions'),
|
|
243
|
-
detectDataDirs: ({ codexExtraHome } = {}) =>
|
|
265
|
+
detectDataDirs: ({ codexExtraHome, extraRoots } = {}) => (
|
|
266
|
+
findCodexDataDirs(codexExtraHome, extraRootList(extraRoots?.codex))
|
|
267
|
+
),
|
|
244
268
|
},
|
|
245
269
|
{
|
|
246
270
|
name: 'Grok',
|
|
247
271
|
id: 'grok',
|
|
248
272
|
dataDir: join(homedir(), '.grok', 'sessions'),
|
|
249
|
-
detectDataDirs: findGrokDataDirs,
|
|
273
|
+
detectDataDirs: ({ extraRoots } = {}) => findGrokDataDirs(extraRootList(extraRoots?.grok)),
|
|
250
274
|
},
|
|
251
275
|
{
|
|
252
276
|
name: 'GitHub Copilot CLI',
|
|
@@ -296,7 +320,9 @@ export const TOOLS = [
|
|
|
296
320
|
name: 'pi',
|
|
297
321
|
id: 'pi-coding-agent',
|
|
298
322
|
dataDir: join(homedir(), '.pi', 'agent', 'sessions'),
|
|
299
|
-
detectDataDirs:
|
|
323
|
+
detectDataDirs: ({ extraRoots } = {}) => (
|
|
324
|
+
findPiDataDirs(extraRootList(extraRoots?.['pi-coding-agent']))
|
|
325
|
+
),
|
|
300
326
|
},
|
|
301
327
|
{
|
|
302
328
|
name: 'Qwen Code',
|
|
@@ -311,6 +337,12 @@ export const TOOLS = [
|
|
|
311
337
|
dataDir: join(homedir(), '.kimi-code', 'sessions'),
|
|
312
338
|
detectDataDirs: findKimiCodeDataDirs,
|
|
313
339
|
},
|
|
340
|
+
{
|
|
341
|
+
name: 'MiniMax Code',
|
|
342
|
+
id: 'mcode',
|
|
343
|
+
dataDir: join(homedir(), '.minimax', 'v2', 'sqlite', 'runtime-state.sqlite'),
|
|
344
|
+
detectDataDirs: () => [getMcodeDbPath()].filter(existsSync),
|
|
345
|
+
},
|
|
314
346
|
{
|
|
315
347
|
name: 'MiMoCode',
|
|
316
348
|
id: 'mimocode',
|
|
@@ -337,7 +369,9 @@ export const TOOLS = [
|
|
|
337
369
|
name: 'Antigravity',
|
|
338
370
|
id: 'antigravity',
|
|
339
371
|
dataDir: join(homedir(), '.gemini', 'antigravity'),
|
|
340
|
-
detectDataDirs:
|
|
372
|
+
detectDataDirs: ({ extraRoots } = {}) => (
|
|
373
|
+
findAntigravityDataDirs(extraRootList(extraRoots?.antigravity))
|
|
374
|
+
),
|
|
341
375
|
},
|
|
342
376
|
{
|
|
343
377
|
name: 'Trae CLI',
|