@ai-studio-3d/vyasa 0.1.0
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 +69 -0
- package/data/common-kit.json +34 -0
- package/data/pricing.json +119 -0
- package/data/schema/AnalyticsData.json +214 -0
- package/data/schema/FoundationData.json +42 -0
- package/data/schema/KitData.json +321 -0
- package/data/schema/SearchData.json +173 -0
- package/data/schema/SessionDetailData.json +231 -0
- package/data/schema/SessionsListData.json +115 -0
- package/data/schema/WasteData.json +95 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +343 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/billing/index.d.ts +4 -0
- package/dist/core/billing/index.d.ts.map +1 -0
- package/dist/core/billing/index.js +43 -0
- package/dist/core/billing/index.js.map +1 -0
- package/dist/core/budget/index.d.ts +30 -0
- package/dist/core/budget/index.d.ts.map +1 -0
- package/dist/core/budget/index.js +61 -0
- package/dist/core/budget/index.js.map +1 -0
- package/dist/core/codex/index.d.ts +3 -0
- package/dist/core/codex/index.d.ts.map +1 -0
- package/dist/core/codex/index.js +212 -0
- package/dist/core/codex/index.js.map +1 -0
- package/dist/core/cursor/index.d.ts +3 -0
- package/dist/core/cursor/index.d.ts.map +1 -0
- package/dist/core/cursor/index.js +304 -0
- package/dist/core/cursor/index.js.map +1 -0
- package/dist/core/cursor/scan.d.ts +28 -0
- package/dist/core/cursor/scan.d.ts.map +1 -0
- package/dist/core/cursor/scan.js +79 -0
- package/dist/core/cursor/scan.js.map +1 -0
- package/dist/core/decisions/index.d.ts +21 -0
- package/dist/core/decisions/index.d.ts.map +1 -0
- package/dist/core/decisions/index.js +133 -0
- package/dist/core/decisions/index.js.map +1 -0
- package/dist/core/dlp/index.d.ts +7 -0
- package/dist/core/dlp/index.d.ts.map +1 -0
- package/dist/core/dlp/index.js +115 -0
- package/dist/core/dlp/index.js.map +1 -0
- package/dist/core/health/index.d.ts +10 -0
- package/dist/core/health/index.d.ts.map +1 -0
- package/dist/core/health/index.js +21 -0
- package/dist/core/health/index.js.map +1 -0
- package/dist/core/index/index.d.ts +9 -0
- package/dist/core/index/index.d.ts.map +1 -0
- package/dist/core/index/index.js +103 -0
- package/dist/core/index/index.js.map +1 -0
- package/dist/core/index/ingest.d.ts +16 -0
- package/dist/core/index/ingest.d.ts.map +1 -0
- package/dist/core/index/ingest.js +184 -0
- package/dist/core/index/ingest.js.map +1 -0
- package/dist/core/index/platforms.d.ts +21 -0
- package/dist/core/index/platforms.d.ts.map +1 -0
- package/dist/core/index/platforms.js +44 -0
- package/dist/core/index/platforms.js.map +1 -0
- package/dist/core/index/pricing-map.d.ts +10 -0
- package/dist/core/index/pricing-map.d.ts.map +1 -0
- package/dist/core/index/pricing-map.js +16 -0
- package/dist/core/index/pricing-map.js.map +1 -0
- package/dist/core/index/progress.d.ts +26 -0
- package/dist/core/index/progress.d.ts.map +1 -0
- package/dist/core/index/progress.js +59 -0
- package/dist/core/index/progress.js.map +1 -0
- package/dist/core/index/scan.d.ts +9 -0
- package/dist/core/index/scan.d.ts.map +1 -0
- package/dist/core/index/scan.js +42 -0
- package/dist/core/index/scan.js.map +1 -0
- package/dist/core/index/statements.d.ts +18 -0
- package/dist/core/index/statements.d.ts.map +1 -0
- package/dist/core/index/statements.js +82 -0
- package/dist/core/index/statements.js.map +1 -0
- package/dist/core/index/tail.d.ts +42 -0
- package/dist/core/index/tail.d.ts.map +1 -0
- package/dist/core/index/tail.js +123 -0
- package/dist/core/index/tail.js.map +1 -0
- package/dist/core/jsonl/index.d.ts +3 -0
- package/dist/core/jsonl/index.d.ts.map +1 -0
- package/dist/core/jsonl/index.js +106 -0
- package/dist/core/jsonl/index.js.map +1 -0
- package/dist/core/jsonl-validate/index.d.ts +10 -0
- package/dist/core/jsonl-validate/index.d.ts.map +1 -0
- package/dist/core/jsonl-validate/index.js +40 -0
- package/dist/core/jsonl-validate/index.js.map +1 -0
- package/dist/core/kit/detection.d.ts +94 -0
- package/dist/core/kit/detection.d.ts.map +1 -0
- package/dist/core/kit/detection.js +312 -0
- package/dist/core/kit/detection.js.map +1 -0
- package/dist/core/kit/index.d.ts +12 -0
- package/dist/core/kit/index.d.ts.map +1 -0
- package/dist/core/kit/index.js +131 -0
- package/dist/core/kit/index.js.map +1 -0
- package/dist/core/kit/scan.d.ts +28 -0
- package/dist/core/kit/scan.d.ts.map +1 -0
- package/dist/core/kit/scan.js +432 -0
- package/dist/core/kit/scan.js.map +1 -0
- package/dist/core/recap/index.d.ts +18 -0
- package/dist/core/recap/index.d.ts.map +1 -0
- package/dist/core/recap/index.js +88 -0
- package/dist/core/recap/index.js.map +1 -0
- package/dist/core/repetition/index.d.ts +5 -0
- package/dist/core/repetition/index.d.ts.map +1 -0
- package/dist/core/repetition/index.js +0 -0
- package/dist/core/repetition/index.js.map +1 -0
- package/dist/core/schema/index.d.ts +514 -0
- package/dist/core/schema/index.d.ts.map +1 -0
- package/dist/core/schema/index.js +2 -0
- package/dist/core/schema/index.js.map +1 -0
- package/dist/core/schema-watch/index.d.ts +34 -0
- package/dist/core/schema-watch/index.d.ts.map +1 -0
- package/dist/core/schema-watch/index.js +87 -0
- package/dist/core/schema-watch/index.js.map +1 -0
- package/dist/core/search/index.d.ts +44 -0
- package/dist/core/search/index.d.ts.map +1 -0
- package/dist/core/search/index.js +641 -0
- package/dist/core/search/index.js.map +1 -0
- package/dist/core/settings/index.d.ts +24 -0
- package/dist/core/settings/index.d.ts.map +1 -0
- package/dist/core/settings/index.js +76 -0
- package/dist/core/settings/index.js.map +1 -0
- package/dist/core/share/bundle.d.ts +40 -0
- package/dist/core/share/bundle.d.ts.map +1 -0
- package/dist/core/share/bundle.js +158 -0
- package/dist/core/share/bundle.js.map +1 -0
- package/dist/core/share/canonical/from-claude.d.ts +13 -0
- package/dist/core/share/canonical/from-claude.d.ts.map +1 -0
- package/dist/core/share/canonical/from-claude.js +129 -0
- package/dist/core/share/canonical/from-claude.js.map +1 -0
- package/dist/core/share/canonical/from-codex.d.ts +18 -0
- package/dist/core/share/canonical/from-codex.d.ts.map +1 -0
- package/dist/core/share/canonical/from-codex.js +183 -0
- package/dist/core/share/canonical/from-codex.js.map +1 -0
- package/dist/core/share/canonical/to-claude.d.ts +15 -0
- package/dist/core/share/canonical/to-claude.d.ts.map +1 -0
- package/dist/core/share/canonical/to-claude.js +146 -0
- package/dist/core/share/canonical/to-claude.js.map +1 -0
- package/dist/core/share/canonical/to-codex.d.ts +21 -0
- package/dist/core/share/canonical/to-codex.d.ts.map +1 -0
- package/dist/core/share/canonical/to-codex.js +124 -0
- package/dist/core/share/canonical/to-codex.js.map +1 -0
- package/dist/core/share/canonical/tool-map.d.ts +61 -0
- package/dist/core/share/canonical/tool-map.d.ts.map +1 -0
- package/dist/core/share/canonical/tool-map.js +299 -0
- package/dist/core/share/canonical/tool-map.js.map +1 -0
- package/dist/core/share/canonical/types.d.ts +57 -0
- package/dist/core/share/canonical/types.d.ts.map +1 -0
- package/dist/core/share/canonical/types.js +9 -0
- package/dist/core/share/canonical/types.js.map +1 -0
- package/dist/core/share/import.d.ts +28 -0
- package/dist/core/share/import.d.ts.map +1 -0
- package/dist/core/share/import.js +174 -0
- package/dist/core/share/import.js.map +1 -0
- package/dist/core/share/manifest.d.ts +37 -0
- package/dist/core/share/manifest.d.ts.map +1 -0
- package/dist/core/share/manifest.js +31 -0
- package/dist/core/share/manifest.js.map +1 -0
- package/dist/core/share/preview.d.ts +4 -0
- package/dist/core/share/preview.d.ts.map +1 -0
- package/dist/core/share/preview.js +153 -0
- package/dist/core/share/preview.js.map +1 -0
- package/dist/core/share/primer.d.ts +19 -0
- package/dist/core/share/primer.d.ts.map +1 -0
- package/dist/core/share/primer.js +196 -0
- package/dist/core/share/primer.js.map +1 -0
- package/dist/core/share/resume.d.ts +10 -0
- package/dist/core/share/resume.d.ts.map +1 -0
- package/dist/core/share/resume.js +58 -0
- package/dist/core/share/resume.js.map +1 -0
- package/dist/core/share/scrub.d.ts +10 -0
- package/dist/core/share/scrub.d.ts.map +1 -0
- package/dist/core/share/scrub.js +198 -0
- package/dist/core/share/scrub.js.map +1 -0
- package/dist/core/share/tar.d.ts +12 -0
- package/dist/core/share/tar.d.ts.map +1 -0
- package/dist/core/share/tar.js +78 -0
- package/dist/core/share/tar.js.map +1 -0
- package/dist/core/tags/index.d.ts +48 -0
- package/dist/core/tags/index.d.ts.map +1 -0
- package/dist/core/tags/index.js +113 -0
- package/dist/core/tags/index.js.map +1 -0
- package/dist/core/today/index.d.ts +25 -0
- package/dist/core/today/index.d.ts.map +1 -0
- package/dist/core/today/index.js +42 -0
- package/dist/core/today/index.js.map +1 -0
- package/dist/core/waste/abandoned.d.ts +12 -0
- package/dist/core/waste/abandoned.d.ts.map +1 -0
- package/dist/core/waste/abandoned.js +127 -0
- package/dist/core/waste/abandoned.js.map +1 -0
- package/dist/core/waste/cache-miss.d.ts +9 -0
- package/dist/core/waste/cache-miss.d.ts.map +1 -0
- package/dist/core/waste/cache-miss.js +84 -0
- package/dist/core/waste/cache-miss.js.map +1 -0
- package/dist/core/waste/index.d.ts +12 -0
- package/dist/core/waste/index.d.ts.map +1 -0
- package/dist/core/waste/index.js +45 -0
- package/dist/core/waste/index.js.map +1 -0
- package/dist/core/waste/wrong-model.d.ts +11 -0
- package/dist/core/waste/wrong-model.d.ts.map +1 -0
- package/dist/core/waste/wrong-model.js +104 -0
- package/dist/core/waste/wrong-model.js.map +1 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +29 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +8 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +282 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/server/events.d.ts +4 -0
- package/dist/server/events.d.ts.map +1 -0
- package/dist/server/events.js +54 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +238 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes/foundation.d.ts +2 -0
- package/dist/server/routes/foundation.d.ts.map +1 -0
- package/dist/server/routes/foundation.js +2 -0
- package/dist/server/routes/foundation.js.map +1 -0
- package/dist/server/routes/search.d.ts +2 -0
- package/dist/server/routes/search.d.ts.map +1 -0
- package/dist/server/routes/search.js +2 -0
- package/dist/server/routes/search.js.map +1 -0
- package/dist/server/routes/sessions.d.ts +2 -0
- package/dist/server/routes/sessions.d.ts.map +1 -0
- package/dist/server/routes/sessions.js +2 -0
- package/dist/server/routes/sessions.js.map +1 -0
- package/dist/server/routes/waste.d.ts +2 -0
- package/dist/server/routes/waste.d.ts.map +1 -0
- package/dist/server/routes/waste.js +2 -0
- package/dist/server/routes/waste.js.map +1 -0
- package/dist/web/assets/index-Ba1VvTj0.js +37 -0
- package/dist/web/index.html +12 -0
- package/package.json +76 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { statSync } from 'node:fs';
|
|
3
|
+
import chokidar from 'chokidar';
|
|
4
|
+
import { parseJsonl } from '../jsonl/index.js';
|
|
5
|
+
import { parseCodex } from '../codex/index.js';
|
|
6
|
+
import { ingestFromParsed } from './ingest.js';
|
|
7
|
+
import { prepareIngestStatements } from './statements.js';
|
|
8
|
+
import { loadPricingMap } from './pricing-map.js';
|
|
9
|
+
import { indexProgress } from './progress.js';
|
|
10
|
+
// Claude/Codex flush several JSONL lines per turn (one fsync each). 300ms
|
|
11
|
+
// covers the burst so we re-ingest once per turn, not once per line.
|
|
12
|
+
const DEBOUNCE_MS = 300;
|
|
13
|
+
/**
|
|
14
|
+
* Watches `~/.claude/projects/**\/*.jsonl` and `~/.codex/sessions/**\/*.jsonl`
|
|
15
|
+
* for live writes. Re-ingests changed files (debounced 300ms) and emits
|
|
16
|
+
* `'session-updated'` so the dashboard can refresh.
|
|
17
|
+
*
|
|
18
|
+
* Construction is cheap — call `.start()` after `buildIndex` finishes so we
|
|
19
|
+
* don't race with the initial bulk ingest.
|
|
20
|
+
*/
|
|
21
|
+
export class TailWatcher extends EventEmitter {
|
|
22
|
+
db;
|
|
23
|
+
roots;
|
|
24
|
+
stmts;
|
|
25
|
+
pricingMap;
|
|
26
|
+
watchers = [];
|
|
27
|
+
timers = new Map();
|
|
28
|
+
inFlight = new Map();
|
|
29
|
+
started = false;
|
|
30
|
+
closed = false;
|
|
31
|
+
constructor(db, roots) {
|
|
32
|
+
super();
|
|
33
|
+
this.db = db;
|
|
34
|
+
this.roots = roots;
|
|
35
|
+
// Prepare once — statements and pricing map are reusable across ingests.
|
|
36
|
+
this.stmts = prepareIngestStatements(db);
|
|
37
|
+
this.pricingMap = loadPricingMap(db);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Begin watching. Safe to call once. With `ignoreInitial: true` chokidar
|
|
41
|
+
* skips the existing tree and only fires on subsequent add/change events.
|
|
42
|
+
*/
|
|
43
|
+
start() {
|
|
44
|
+
if (this.started || this.closed)
|
|
45
|
+
return;
|
|
46
|
+
this.started = true;
|
|
47
|
+
for (const root of this.roots) {
|
|
48
|
+
// chokidar v4 dropped glob support — watch the dir and filter via `ignored`.
|
|
49
|
+
const watcher = chokidar.watch(root.rootDir, {
|
|
50
|
+
ignoreInitial: true,
|
|
51
|
+
persistent: true,
|
|
52
|
+
ignored: (path, stats) => !!stats?.isFile() && !path.endsWith('.jsonl'),
|
|
53
|
+
});
|
|
54
|
+
const onChange = (path) => this.schedule(path, root.platform);
|
|
55
|
+
watcher.on('add', onChange);
|
|
56
|
+
watcher.on('change', onChange);
|
|
57
|
+
watcher.on('error', (err) => {
|
|
58
|
+
console.error('[vyasa] tail watcher error:', err);
|
|
59
|
+
});
|
|
60
|
+
this.watchers.push(watcher);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Debounce per-path so burst writes during a single turn coalesce into one ingest. */
|
|
64
|
+
schedule(path, platform) {
|
|
65
|
+
const existing = this.timers.get(path);
|
|
66
|
+
if (existing)
|
|
67
|
+
clearTimeout(existing);
|
|
68
|
+
const timer = setTimeout(() => {
|
|
69
|
+
this.timers.delete(path);
|
|
70
|
+
// Serialize per-path: if an ingest for this file is still running, queue
|
|
71
|
+
// ours after it instead of racing two parses of the same file in parallel
|
|
72
|
+
// (the later one would clobber the earlier one with possibly-stale data).
|
|
73
|
+
const previous = this.inFlight.get(path) ?? Promise.resolve();
|
|
74
|
+
const next = previous.catch(() => { }).then(() => this.ingest(path, platform));
|
|
75
|
+
this.inFlight.set(path, next);
|
|
76
|
+
void next.finally(() => {
|
|
77
|
+
if (this.inFlight.get(path) === next)
|
|
78
|
+
this.inFlight.delete(path);
|
|
79
|
+
});
|
|
80
|
+
}, DEBOUNCE_MS);
|
|
81
|
+
this.timers.set(path, timer);
|
|
82
|
+
}
|
|
83
|
+
async ingest(path, platform) {
|
|
84
|
+
if (this.closed)
|
|
85
|
+
return;
|
|
86
|
+
try {
|
|
87
|
+
let mtimeMs;
|
|
88
|
+
try {
|
|
89
|
+
mtimeMs = statSync(path).mtimeMs;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// File was removed between event and ingest — nothing to do.
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const parse = platform === 'codex' ? parseCodex : parseJsonl;
|
|
96
|
+
const parsed = await parse(path);
|
|
97
|
+
if (!parsed.sessionId)
|
|
98
|
+
return;
|
|
99
|
+
this.db.transaction(() => ingestFromParsed(this.db, this.stmts, this.pricingMap, path, mtimeMs, platform, parsed))();
|
|
100
|
+
const event = { sessionId: parsed.sessionId, platform, path };
|
|
101
|
+
this.emit('session-updated', event);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
indexProgress.recordError();
|
|
105
|
+
console.error(`[vyasa] tail ingest failed for ${path}:`, err);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async close() {
|
|
109
|
+
if (this.closed)
|
|
110
|
+
return;
|
|
111
|
+
this.closed = true;
|
|
112
|
+
for (const timer of this.timers.values())
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
this.timers.clear();
|
|
115
|
+
// Drain in-flight ingests so we don't write to a closed DB after shutdown.
|
|
116
|
+
await Promise.allSettled([...this.inFlight.values()]);
|
|
117
|
+
this.inFlight.clear();
|
|
118
|
+
await Promise.all(this.watchers.map((w) => w.close()));
|
|
119
|
+
this.watchers = [];
|
|
120
|
+
this.removeAllListeners();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=tail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tail.js","sourceRoot":"","sources":["../../../src/core/index/tail.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/jsonl/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,aAAa,EAKd,MAAM,oBAAoB,CAAA;AAM3B,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAuGzE"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
import { assertString } from '../jsonl-validate/index.js';
|
|
4
|
+
import { recordFingerprint } from '../schema-watch/index.js';
|
|
5
|
+
const SKIP_TYPES = new Set(['queue-operation', 'attachment', 'last-prompt']);
|
|
6
|
+
export async function parseJsonl(filePath) {
|
|
7
|
+
const records = [];
|
|
8
|
+
const parseErrors = [];
|
|
9
|
+
const fingerprints = new Map();
|
|
10
|
+
let sessionId = null;
|
|
11
|
+
let startedAt = null;
|
|
12
|
+
let endedAt = null;
|
|
13
|
+
let claudeCustomTitle = null;
|
|
14
|
+
let claudeAiTitle = null;
|
|
15
|
+
let lineNum = 0;
|
|
16
|
+
const rl = createInterface({
|
|
17
|
+
input: createReadStream(filePath, { encoding: 'utf8' }),
|
|
18
|
+
crlfDelay: Infinity,
|
|
19
|
+
});
|
|
20
|
+
for await (const line of rl) {
|
|
21
|
+
lineNum++;
|
|
22
|
+
const trimmed = line.trim();
|
|
23
|
+
if (!trimmed)
|
|
24
|
+
continue;
|
|
25
|
+
let raw;
|
|
26
|
+
try {
|
|
27
|
+
raw = JSON.parse(trimmed);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// skip malformed lines silently (JSON parse error — no record type to report)
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const type = raw['type'];
|
|
34
|
+
if (!type || SKIP_TYPES.has(type))
|
|
35
|
+
continue;
|
|
36
|
+
// Derive sessionId from first record that has one
|
|
37
|
+
if (!sessionId && typeof raw['sessionId'] === 'string') {
|
|
38
|
+
sessionId = raw['sessionId'];
|
|
39
|
+
}
|
|
40
|
+
// Capture Claude-native session names (latest-wins). These are standalone metadata
|
|
41
|
+
// records, NOT conversation turns — do not push into `records` (would inflate counts).
|
|
42
|
+
// No `continue`: let them fall through to recordFingerprint below (the type branches
|
|
43
|
+
// skip them) so schema-drift detection still observes these record shapes.
|
|
44
|
+
if (type === 'custom-title' && typeof raw['customTitle'] === 'string')
|
|
45
|
+
claudeCustomTitle = raw['customTitle'];
|
|
46
|
+
if (type === 'ai-title' && typeof raw['aiTitle'] === 'string')
|
|
47
|
+
claudeAiTitle = raw['aiTitle'];
|
|
48
|
+
try {
|
|
49
|
+
// Collect fingerprint for drift detection (before type-specific handling)
|
|
50
|
+
const fp = recordFingerprint(raw);
|
|
51
|
+
if (!fingerprints.has(fp)) {
|
|
52
|
+
fingerprints.set(fp, { firstSeenLine: lineNum });
|
|
53
|
+
}
|
|
54
|
+
if (type === 'user') {
|
|
55
|
+
// Validate the type field with assertString (validates the role field inside message)
|
|
56
|
+
const msgObj = raw['message'];
|
|
57
|
+
if (msgObj) {
|
|
58
|
+
assertString(msgObj['role'], `line ${lineNum}: message.role`);
|
|
59
|
+
}
|
|
60
|
+
// `toolUseResult` (top-level, sibling to message — carries durationMs for some tools) and
|
|
61
|
+
// `permissionMode` ride through the cast since they're top-level keys on the raw record.
|
|
62
|
+
// is_error lives on the tool_result content block, already typed on ContentBlock.
|
|
63
|
+
const record = raw;
|
|
64
|
+
records.push(record);
|
|
65
|
+
if (startedAt === null) {
|
|
66
|
+
startedAt = record.timestamp;
|
|
67
|
+
}
|
|
68
|
+
endedAt = record.timestamp;
|
|
69
|
+
}
|
|
70
|
+
else if (type === 'assistant') {
|
|
71
|
+
const record = raw;
|
|
72
|
+
records.push(record);
|
|
73
|
+
endedAt = record.timestamp;
|
|
74
|
+
}
|
|
75
|
+
else if (type === 'system') {
|
|
76
|
+
const record = raw;
|
|
77
|
+
records.push(record);
|
|
78
|
+
endedAt = record.timestamp;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
parseErrors.push({
|
|
83
|
+
lineNum,
|
|
84
|
+
recordType: type ?? null,
|
|
85
|
+
error: e.message,
|
|
86
|
+
sampleJson: trimmed.slice(0, 200),
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const usageRecords = records.filter((r) => r.type === 'assistant' && r.message.usage != null);
|
|
92
|
+
return {
|
|
93
|
+
sessionId,
|
|
94
|
+
filePath,
|
|
95
|
+
project: null,
|
|
96
|
+
records,
|
|
97
|
+
usageRecords,
|
|
98
|
+
startedAt,
|
|
99
|
+
endedAt,
|
|
100
|
+
claudeCustomTitle,
|
|
101
|
+
claudeAiTitle,
|
|
102
|
+
parseErrors,
|
|
103
|
+
fingerprints,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/jsonl/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validators for JSONL record fields.
|
|
3
|
+
* Used by both the Claude (src/core/jsonl) and Codex (src/core/codex) parsers.
|
|
4
|
+
*/
|
|
5
|
+
export declare function assertString(v: unknown, ctx: string): string;
|
|
6
|
+
export declare function assertObject(v: unknown, ctx: string): Record<string, unknown>;
|
|
7
|
+
export declare function assertOneOf<T extends string>(v: unknown, allowed: readonly T[], ctx: string): T;
|
|
8
|
+
export declare function optionalString(v: unknown, ctx: string): string | undefined;
|
|
9
|
+
export declare function optionalNumber(v: unknown, ctx: string): number | undefined;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/jsonl-validate/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAK5D;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAM7E;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,CAAC,EAAE,OAAO,EACV,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,GAAG,EAAE,MAAM,GACV,CAAC,CAQH;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM1E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validators for JSONL record fields.
|
|
3
|
+
* Used by both the Claude (src/core/jsonl) and Codex (src/core/codex) parsers.
|
|
4
|
+
*/
|
|
5
|
+
export function assertString(v, ctx) {
|
|
6
|
+
if (typeof v !== 'string') {
|
|
7
|
+
throw new Error(`${ctx}: expected string, got ${typeof v}`);
|
|
8
|
+
}
|
|
9
|
+
return v;
|
|
10
|
+
}
|
|
11
|
+
export function assertObject(v, ctx) {
|
|
12
|
+
if (v === null || typeof v !== 'object' || Array.isArray(v)) {
|
|
13
|
+
const got = v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v;
|
|
14
|
+
throw new Error(`${ctx}: expected object, got ${got}`);
|
|
15
|
+
}
|
|
16
|
+
return v;
|
|
17
|
+
}
|
|
18
|
+
export function assertOneOf(v, allowed, ctx) {
|
|
19
|
+
if (typeof v !== 'string') {
|
|
20
|
+
throw new Error(`${ctx}: expected string, got ${typeof v}`);
|
|
21
|
+
}
|
|
22
|
+
if (!allowed.includes(v)) {
|
|
23
|
+
throw new Error(`${ctx}: expected one of [${allowed.join(', ')}], got "${v}"`);
|
|
24
|
+
}
|
|
25
|
+
return v;
|
|
26
|
+
}
|
|
27
|
+
export function optionalString(v, ctx) {
|
|
28
|
+
if (v === null || v === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
return assertString(v, ctx);
|
|
31
|
+
}
|
|
32
|
+
export function optionalNumber(v, ctx) {
|
|
33
|
+
if (v === null || v === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (typeof v !== 'number') {
|
|
36
|
+
throw new Error(`${ctx}: expected number, got ${typeof v}`);
|
|
37
|
+
}
|
|
38
|
+
return v;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/jsonl-validate/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,YAAY,CAAC,CAAU,EAAE,GAAW;IAClD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,0BAA0B,OAAO,CAAC,EAAE,CAAC,CAAA;IAC7D,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAU,EAAE,GAAW;IAClD,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACvE,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,0BAA0B,GAAG,EAAE,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,CAA4B,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,CAAU,EACV,OAAqB,EACrB,GAAW;IAEX,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,0BAA0B,OAAO,CAAC,EAAE,CAAC,CAAA;IAC7D,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAM,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,sBAAsB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IAChF,CAAC;IACD,OAAO,CAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,CAAU,EAAE,GAAW;IACpD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACnD,OAAO,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,CAAU,EAAE,GAAW;IACpD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACnD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,0BAA0B,OAAO,CAAC,EAAE,CAAC,CAAA;IAC7D,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import type { SkillState, McpState, GapItem, ZombieItem } from '../schema/index.js';
|
|
3
|
+
interface ToolUsageRow {
|
|
4
|
+
tool: string;
|
|
5
|
+
input_json: string | null;
|
|
6
|
+
ts: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Returns per-skill usage statistics over the lookback window.
|
|
10
|
+
* Skill firings are tool_calls with tool='Skill' and input_json containing
|
|
11
|
+
* {"skill": "<plugin>:<name>"} (plugin-installed)
|
|
12
|
+
* {"skill": "<name>"} (loose)
|
|
13
|
+
* The canonical id from the call is the join key.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadSkillUsage(db: Database.Database, lookbackDays: number): Map<string, {
|
|
16
|
+
count: number;
|
|
17
|
+
lastUsedMs: number;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Returns per-MCP-server usage statistics over the lookback window. Server
|
|
21
|
+
* name is the token between the first two '__' in the tool name (e.g.
|
|
22
|
+
* `mcp__chrome-devtools__navigate_page` → 'chrome-devtools').
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadMcpUsage(db: Database.Database, lookbackDays: number): Map<string, {
|
|
25
|
+
count: number;
|
|
26
|
+
lastUsedMs: number;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Returns every non-Skill, non-MCP tool_call in the window paired with its
|
|
30
|
+
* input_json. Used by the gap detector to grep for "replaces" patterns.
|
|
31
|
+
* Limited to the tool names we currently emit patterns against, to avoid
|
|
32
|
+
* pulling 100K+ Read/Edit rows.
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadCandidateToolCalls(db: Database.Database, lookbackDays: number, toolFilter: string[]): ToolUsageRow[];
|
|
35
|
+
/**
|
|
36
|
+
* Annotates each SkillState with its usage stats from the tool_calls table.
|
|
37
|
+
*
|
|
38
|
+
* Plugin skills can fire under EITHER '<plugin>:<name>' OR bare '<name>' depending
|
|
39
|
+
* on how the user invoked them — verified in real data (e.g. `update-config`
|
|
40
|
+
* appears as bare even though it's plugin-bundled). Merge both keys so we don't
|
|
41
|
+
* surface false-positive zombies for skills that are actually being used.
|
|
42
|
+
*/
|
|
43
|
+
export declare function hydrateSkillUsage(skills: SkillState[], usage: Map<string, {
|
|
44
|
+
count: number;
|
|
45
|
+
lastUsedMs: number;
|
|
46
|
+
}>): SkillState[];
|
|
47
|
+
export declare function hydrateMcpUsage(mcps: McpState[], usage: Map<string, {
|
|
48
|
+
count: number;
|
|
49
|
+
lastUsedMs: number;
|
|
50
|
+
}>): McpState[];
|
|
51
|
+
export interface CommonKitSkill {
|
|
52
|
+
id: string;
|
|
53
|
+
purpose: string;
|
|
54
|
+
install: string;
|
|
55
|
+
}
|
|
56
|
+
export interface CommonKitMcp {
|
|
57
|
+
id: string;
|
|
58
|
+
purpose: string;
|
|
59
|
+
install: string;
|
|
60
|
+
/** Tool-pattern strings: "<tool>:<pattern>". `tool` matches tool_calls.tool exactly. */
|
|
61
|
+
replaces?: string[];
|
|
62
|
+
}
|
|
63
|
+
export interface CommonKit {
|
|
64
|
+
version: number;
|
|
65
|
+
skills: CommonKitSkill[];
|
|
66
|
+
mcpServers: CommonKitMcp[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* For each common-kit item not already installed, build a GapItem. For MCP
|
|
70
|
+
* gaps, count tool_calls matching the `replaces` patterns over the lookback
|
|
71
|
+
* window — these are loosely the calls an MCP server would handle directly,
|
|
72
|
+
* eliminating the need for shelling out / fetching.
|
|
73
|
+
*
|
|
74
|
+
* Savings heuristic for MCP: we currently surface the match count as a
|
|
75
|
+
* "potential reduction" signal (rendered in the UI as "N calls would be
|
|
76
|
+
* replaced") but do NOT compute dollar savings — Bash/WebFetch cost lives in
|
|
77
|
+
* the assistant's output tokens around the call, which we don't attribute
|
|
78
|
+
* per-tool. So potentialSavingsCents stays 0 in V1 unless the kit grows entries
|
|
79
|
+
* with a model-aware estimate; matchingCallCount is the actionable number.
|
|
80
|
+
*/
|
|
81
|
+
export declare function detectGaps(kit: CommonKit, installedSkillIds: Set<string>, installedMcpIds: Set<string>, candidateCalls: ToolUsageRow[]): GapItem[];
|
|
82
|
+
/** Returns the union of tool names referenced by the kit's `replaces` patterns. */
|
|
83
|
+
export declare function collectReplacedTools(kit: CommonKit): string[];
|
|
84
|
+
/**
|
|
85
|
+
* A skill or MCP server is a "zombie" when it's installed on the filesystem
|
|
86
|
+
* but has not been invoked in the lookback window. We treat usageCount=0 as
|
|
87
|
+
* the threshold (the spec mentions "below threshold like 2 over 30d for skills"
|
|
88
|
+
* — but for V1 we use the strict zero gate since the user has few-to-no skills
|
|
89
|
+
* installed, and a single intentional use is enough to "save" a skill from the
|
|
90
|
+
* list).
|
|
91
|
+
*/
|
|
92
|
+
export declare function detectZombies(skills: SkillState[], mcp: McpState[]): ZombieItem[];
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../../src/core/kit/detection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAA;AACrC,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,OAAO,EACP,UAAU,EACX,MAAM,oBAAoB,CAAA;AAgB3B,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,YAAY,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAuCpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,YAAY,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAiCpD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAAE,GACnB,YAAY,EAAE,CAchB;AAID;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,GACxD,UAAU,EAAE,CASd;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EAAE,EAChB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,GACxD,QAAQ,EAAE,CAMZ;AAID,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,UAAU,EAAE,YAAY,EAAE,CAAA;CAC3B;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,SAAS,EACd,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,EAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,cAAc,EAAE,YAAY,EAAE,GAC7B,OAAO,EAAE,CAqDX;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,EAAE,CAS7D;AAID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,EAAE,EACpB,GAAG,EAAE,QAAQ,EAAE,GACd,UAAU,EAAE,CAmCd"}
|