@nocoo/pew 0.4.0 → 0.6.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.
Files changed (72) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +117 -8
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/session-sync.d.ts +62 -0
  5. package/dist/commands/session-sync.d.ts.map +1 -0
  6. package/dist/commands/session-sync.js +443 -0
  7. package/dist/commands/session-sync.js.map +1 -0
  8. package/dist/commands/session-upload.d.ts +55 -0
  9. package/dist/commands/session-upload.d.ts.map +1 -0
  10. package/dist/commands/session-upload.js +177 -0
  11. package/dist/commands/session-upload.js.map +1 -0
  12. package/dist/commands/status.d.ts +9 -0
  13. package/dist/commands/status.d.ts.map +1 -1
  14. package/dist/commands/status.js +22 -11
  15. package/dist/commands/status.js.map +1 -1
  16. package/dist/commands/sync.d.ts +12 -0
  17. package/dist/commands/sync.d.ts.map +1 -1
  18. package/dist/commands/sync.js +159 -3
  19. package/dist/commands/sync.js.map +1 -1
  20. package/dist/discovery/sources.d.ts +5 -0
  21. package/dist/discovery/sources.d.ts.map +1 -1
  22. package/dist/discovery/sources.js +7 -0
  23. package/dist/discovery/sources.js.map +1 -1
  24. package/dist/parsers/claude-session.d.ts +19 -0
  25. package/dist/parsers/claude-session.d.ts.map +1 -0
  26. package/dist/parsers/claude-session.js +131 -0
  27. package/dist/parsers/claude-session.js.map +1 -0
  28. package/dist/parsers/codex-session.d.ts +24 -0
  29. package/dist/parsers/codex-session.d.ts.map +1 -0
  30. package/dist/parsers/codex-session.js +140 -0
  31. package/dist/parsers/codex-session.js.map +1 -0
  32. package/dist/parsers/codex.d.ts +37 -0
  33. package/dist/parsers/codex.d.ts.map +1 -0
  34. package/dist/parsers/codex.js +136 -0
  35. package/dist/parsers/codex.js.map +1 -0
  36. package/dist/parsers/gemini-session.d.ts +19 -0
  37. package/dist/parsers/gemini-session.d.ts.map +1 -0
  38. package/dist/parsers/gemini-session.js +103 -0
  39. package/dist/parsers/gemini-session.js.map +1 -0
  40. package/dist/parsers/openclaw-session.d.ts +20 -0
  41. package/dist/parsers/openclaw-session.d.ts.map +1 -0
  42. package/dist/parsers/openclaw-session.js +122 -0
  43. package/dist/parsers/openclaw-session.js.map +1 -0
  44. package/dist/parsers/opencode-session.d.ts +15 -0
  45. package/dist/parsers/opencode-session.d.ts.map +1 -0
  46. package/dist/parsers/opencode-session.js +131 -0
  47. package/dist/parsers/opencode-session.js.map +1 -0
  48. package/dist/parsers/opencode-sqlite-db.d.ts +29 -0
  49. package/dist/parsers/opencode-sqlite-db.d.ts.map +1 -0
  50. package/dist/parsers/opencode-sqlite-db.js +71 -0
  51. package/dist/parsers/opencode-sqlite-db.js.map +1 -0
  52. package/dist/parsers/opencode-sqlite-session.d.ts +32 -0
  53. package/dist/parsers/opencode-sqlite-session.d.ts.map +1 -0
  54. package/dist/parsers/opencode-sqlite-session.js +121 -0
  55. package/dist/parsers/opencode-sqlite-session.js.map +1 -0
  56. package/dist/parsers/opencode-sqlite.d.ts +53 -0
  57. package/dist/parsers/opencode-sqlite.d.ts.map +1 -0
  58. package/dist/parsers/opencode-sqlite.js +104 -0
  59. package/dist/parsers/opencode-sqlite.js.map +1 -0
  60. package/dist/storage/session-cursor-store.d.ts +14 -0
  61. package/dist/storage/session-cursor-store.d.ts.map +1 -0
  62. package/dist/storage/session-cursor-store.js +34 -0
  63. package/dist/storage/session-cursor-store.js.map +1 -0
  64. package/dist/storage/session-queue.d.ts +28 -0
  65. package/dist/storage/session-queue.d.ts.map +1 -0
  66. package/dist/storage/session-queue.js +65 -0
  67. package/dist/storage/session-queue.js.map +1 -0
  68. package/dist/utils/paths.d.ts +4 -0
  69. package/dist/utils/paths.d.ts.map +1 -1
  70. package/dist/utils/paths.js +5 -0
  71. package/dist/utils/paths.js.map +1 -1
  72. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Claude Code session collector.
3
+ *
4
+ * Full-scans a Claude JSONL file and extracts session-level metadata.
5
+ * Groups lines by sessionId, counts message types, computes duration.
6
+ */
7
+ import type { SessionSnapshot } from "@pew/core";
8
+ /**
9
+ * Collect session snapshots from a Claude Code JSONL file.
10
+ *
11
+ * Each line may contain a sessionId. Lines are grouped by sessionId,
12
+ * and for each group we produce a SessionSnapshot with:
13
+ * - message counts (user/assistant/total)
14
+ * - wall-clock duration (min timestamp → max timestamp)
15
+ * - last seen model
16
+ * - project ref from file path
17
+ */
18
+ export declare function collectClaudeSessions(filePath: string): Promise<SessionSnapshot[]>;
19
+ //# sourceMappingURL=claude-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-session.d.ts","sourceRoot":"","sources":["../../src/parsers/claude-session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAU,MAAM,WAAW,CAAC;AA2BzD;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,EAAE,CAAC,CA0G5B"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Claude Code session collector.
3
+ *
4
+ * Full-scans a Claude JSONL file and extracts session-level metadata.
5
+ * Groups lines by sessionId, counts message types, computes duration.
6
+ */
7
+ import { createReadStream } from "node:fs";
8
+ import { stat } from "node:fs/promises";
9
+ import { createInterface } from "node:readline";
10
+ /**
11
+ * Extract the project reference from a Claude file path.
12
+ *
13
+ * Claude stores files under ~/.claude/projects/{hash}/{file}.jsonl.
14
+ * We extract the directory name immediately after "projects/".
15
+ * Returns null if the path doesn't contain "projects/".
16
+ */
17
+ function extractProjectRef(filePath) {
18
+ const parts = filePath.split("/");
19
+ const projectsIdx = parts.lastIndexOf("projects");
20
+ if (projectsIdx < 0 || projectsIdx + 1 >= parts.length - 1)
21
+ return null;
22
+ return parts[projectsIdx + 1] || null;
23
+ }
24
+ /**
25
+ * Collect session snapshots from a Claude Code JSONL file.
26
+ *
27
+ * Each line may contain a sessionId. Lines are grouped by sessionId,
28
+ * and for each group we produce a SessionSnapshot with:
29
+ * - message counts (user/assistant/total)
30
+ * - wall-clock duration (min timestamp → max timestamp)
31
+ * - last seen model
32
+ * - project ref from file path
33
+ */
34
+ export async function collectClaudeSessions(filePath) {
35
+ const st = await stat(filePath).catch(() => null);
36
+ if (!st || !st.isFile() || st.size === 0)
37
+ return [];
38
+ const sessions = new Map();
39
+ const stream = createReadStream(filePath, { encoding: "utf8" });
40
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
41
+ try {
42
+ for await (const line of rl) {
43
+ if (!line)
44
+ continue;
45
+ let obj;
46
+ try {
47
+ obj = JSON.parse(line);
48
+ }
49
+ catch {
50
+ continue;
51
+ }
52
+ const sessionId = typeof obj.sessionId === "string" ? obj.sessionId : null;
53
+ if (!sessionId)
54
+ continue;
55
+ const timestamp = typeof obj.timestamp === "string" ? obj.timestamp : null;
56
+ const type = typeof obj.type === "string" ? obj.type : null;
57
+ // Get or create session accumulator
58
+ let accum = sessions.get(sessionId);
59
+ if (!accum) {
60
+ accum = {
61
+ sessionId,
62
+ userMessages: 0,
63
+ assistantMessages: 0,
64
+ totalMessages: 0,
65
+ minTimestamp: null,
66
+ maxTimestamp: null,
67
+ lastModel: null,
68
+ };
69
+ sessions.set(sessionId, accum);
70
+ }
71
+ // Count messages
72
+ accum.totalMessages++;
73
+ if (type === "user") {
74
+ accum.userMessages++;
75
+ }
76
+ else if (type === "assistant") {
77
+ accum.assistantMessages++;
78
+ }
79
+ // Track timestamps
80
+ if (timestamp) {
81
+ if (!accum.minTimestamp || timestamp < accum.minTimestamp) {
82
+ accum.minTimestamp = timestamp;
83
+ }
84
+ if (!accum.maxTimestamp || timestamp > accum.maxTimestamp) {
85
+ accum.maxTimestamp = timestamp;
86
+ }
87
+ }
88
+ // Track model (from message.model or obj.model)
89
+ const msg = obj.message;
90
+ const model = typeof msg?.model === "string"
91
+ ? msg.model.trim()
92
+ : typeof obj.model === "string"
93
+ ? obj.model.trim()
94
+ : null;
95
+ if (model) {
96
+ accum.lastModel = model;
97
+ }
98
+ }
99
+ }
100
+ finally {
101
+ rl.close();
102
+ stream.destroy();
103
+ }
104
+ // Convert accumulators to snapshots
105
+ const projectRef = extractProjectRef(filePath);
106
+ const snapshotAt = new Date().toISOString();
107
+ const results = [];
108
+ for (const accum of sessions.values()) {
109
+ if (!accum.minTimestamp)
110
+ continue; // no valid timestamps → skip
111
+ const startedAt = accum.minTimestamp;
112
+ const lastMessageAt = accum.maxTimestamp ?? accum.minTimestamp;
113
+ const durationMs = new Date(lastMessageAt).getTime() - new Date(startedAt).getTime();
114
+ results.push({
115
+ sessionKey: `claude:${accum.sessionId}`,
116
+ source: "claude-code",
117
+ kind: "human",
118
+ startedAt,
119
+ lastMessageAt,
120
+ durationSeconds: Math.max(0, Math.floor(durationMs / 1000)),
121
+ userMessages: accum.userMessages,
122
+ assistantMessages: accum.assistantMessages,
123
+ totalMessages: accum.totalMessages,
124
+ projectRef,
125
+ model: accum.lastModel,
126
+ snapshotAt,
127
+ });
128
+ }
129
+ return results;
130
+ }
131
+ //# sourceMappingURL=claude-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-session.js","sourceRoot":"","sources":["../../src/parsers/claude-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Codex CLI session collector.
3
+ *
4
+ * Full-scans a Codex JSONL rollout file and produces a single SessionSnapshot.
5
+ * Each rollout file represents one session. Codex is user-driven (kind: "human").
6
+ *
7
+ * Session ID comes from session_meta.payload.id (UUID).
8
+ * Project ref is a SHA-256 hash of session_meta.payload.cwd (privacy-safe).
9
+ * Model comes from turn_context.payload.model (preferred) or session_meta.payload.model (fallback).
10
+ *
11
+ * Message counting:
12
+ * - response_item with payload.role === "user" → userMessages
13
+ * - response_item with payload.role === "assistant" → assistantMessages
14
+ * - All valid JSON lines → totalMessages
15
+ */
16
+ import type { SessionSnapshot } from "@pew/core";
17
+ /**
18
+ * Collect session snapshots from a Codex CLI JSONL rollout file.
19
+ *
20
+ * Reads every line, extracts session metadata, counts messages,
21
+ * and produces 0 or 1 SessionSnapshot.
22
+ */
23
+ export declare function collectCodexSessions(filePath: string): Promise<SessionSnapshot[]>;
24
+ //# sourceMappingURL=codex-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-session.d.ts","sourceRoot":"","sources":["../../src/parsers/codex-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAU,MAAM,WAAW,CAAC;AAEzD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,EAAE,CAAC,CA2H5B"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Codex CLI session collector.
3
+ *
4
+ * Full-scans a Codex JSONL rollout file and produces a single SessionSnapshot.
5
+ * Each rollout file represents one session. Codex is user-driven (kind: "human").
6
+ *
7
+ * Session ID comes from session_meta.payload.id (UUID).
8
+ * Project ref is a SHA-256 hash of session_meta.payload.cwd (privacy-safe).
9
+ * Model comes from turn_context.payload.model (preferred) or session_meta.payload.model (fallback).
10
+ *
11
+ * Message counting:
12
+ * - response_item with payload.role === "user" → userMessages
13
+ * - response_item with payload.role === "assistant" → assistantMessages
14
+ * - All valid JSON lines → totalMessages
15
+ */
16
+ import { createReadStream } from "node:fs";
17
+ import { stat } from "node:fs/promises";
18
+ import { createInterface } from "node:readline";
19
+ import { createHash } from "node:crypto";
20
+ import { resolve } from "node:path";
21
+ /**
22
+ * Collect session snapshots from a Codex CLI JSONL rollout file.
23
+ *
24
+ * Reads every line, extracts session metadata, counts messages,
25
+ * and produces 0 or 1 SessionSnapshot.
26
+ */
27
+ export async function collectCodexSessions(filePath) {
28
+ const st = await stat(filePath).catch(() => null);
29
+ if (!st || !st.isFile() || st.size === 0)
30
+ return [];
31
+ let sessionId = null;
32
+ let projectRef = null;
33
+ let lastModel = null;
34
+ let userMessages = 0;
35
+ let assistantMessages = 0;
36
+ let totalMessages = 0;
37
+ let minTimestamp = null;
38
+ let maxTimestamp = null;
39
+ const stream = createReadStream(filePath, { encoding: "utf8" });
40
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
41
+ try {
42
+ for await (const line of rl) {
43
+ if (!line)
44
+ continue;
45
+ let obj;
46
+ try {
47
+ obj = JSON.parse(line);
48
+ }
49
+ catch {
50
+ continue;
51
+ }
52
+ totalMessages++;
53
+ const type = typeof obj.type === "string" ? obj.type : null;
54
+ const timestamp = typeof obj.timestamp === "string" ? obj.timestamp : null;
55
+ // Track timestamps
56
+ if (timestamp) {
57
+ if (!minTimestamp || timestamp < minTimestamp) {
58
+ minTimestamp = timestamp;
59
+ }
60
+ if (!maxTimestamp || timestamp > maxTimestamp) {
61
+ maxTimestamp = timestamp;
62
+ }
63
+ }
64
+ const payload = obj.payload;
65
+ // Extract session ID and project ref from session_meta
66
+ if (type === "session_meta" && payload) {
67
+ if (typeof payload.id === "string" && payload.id) {
68
+ sessionId = payload.id;
69
+ }
70
+ if (typeof payload.cwd === "string" && payload.cwd) {
71
+ projectRef = createHash("sha256")
72
+ .update(payload.cwd)
73
+ .digest("hex")
74
+ .slice(0, 12);
75
+ }
76
+ // Fallback model from session_meta
77
+ if (typeof payload.model === "string" && payload.model.trim()) {
78
+ lastModel = payload.model.trim();
79
+ }
80
+ continue;
81
+ }
82
+ // Track model from turn_context (overrides session_meta)
83
+ if (type === "turn_context" && payload) {
84
+ if (typeof payload.model === "string" && payload.model.trim()) {
85
+ lastModel = payload.model.trim();
86
+ }
87
+ continue;
88
+ }
89
+ // Count user/assistant messages from response_item
90
+ if (type === "response_item" && payload) {
91
+ const role = typeof payload.role === "string" ? payload.role : null;
92
+ if (role === "user") {
93
+ userMessages++;
94
+ }
95
+ else if (role === "assistant") {
96
+ assistantMessages++;
97
+ }
98
+ }
99
+ }
100
+ }
101
+ finally {
102
+ rl.close();
103
+ stream.destroy();
104
+ }
105
+ // No valid timestamps → can't produce a snapshot
106
+ if (!minTimestamp)
107
+ return [];
108
+ const startedAt = minTimestamp;
109
+ const lastMessageAt = maxTimestamp ?? minTimestamp;
110
+ const durationMs = new Date(lastMessageAt).getTime() - new Date(startedAt).getTime();
111
+ // Session key: prefer native UUID from session_meta, fallback to sha256 of path
112
+ let sessionKey;
113
+ if (sessionId) {
114
+ sessionKey = `codex:${sessionId}`;
115
+ }
116
+ else {
117
+ const hash = createHash("sha256")
118
+ .update(resolve(filePath))
119
+ .digest("hex")
120
+ .slice(0, 16);
121
+ sessionKey = `codex:${hash}`;
122
+ }
123
+ return [
124
+ {
125
+ sessionKey,
126
+ source: "codex",
127
+ kind: "human",
128
+ startedAt,
129
+ lastMessageAt,
130
+ durationSeconds: Math.max(0, Math.floor(durationMs / 1000)),
131
+ userMessages,
132
+ assistantMessages,
133
+ totalMessages,
134
+ projectRef,
135
+ model: lastModel,
136
+ snapshotAt: new Date().toISOString(),
137
+ },
138
+ ];
139
+ }
140
+ //# sourceMappingURL=codex-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-session.js","sourceRoot":"","sources":["../../src/parsers/codex-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Codex CLI token parser.
3
+ *
4
+ * Parses Codex JSONL rollout files (~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl)
5
+ * incrementally from a byte offset.
6
+ *
7
+ * Strategy: Cumulative total_token_usage with diff (like Gemini).
8
+ * Each `event_msg` with `payload.type === "token_count"` contains running totals.
9
+ * We diff consecutive totals to produce per-turn deltas.
10
+ *
11
+ * Model is tracked from `turn_context.payload.model` or `session_meta.payload.model`.
12
+ */
13
+ import type { TokenDelta } from "@pew/core";
14
+ import type { ParsedDelta } from "./claude.js";
15
+ /** Result of parsing a single Codex JSONL rollout file */
16
+ export interface CodexFileResult {
17
+ deltas: ParsedDelta[];
18
+ endOffset: number;
19
+ /** Last seen cumulative totals (for resuming incremental parsing) */
20
+ lastTotals: TokenDelta | null;
21
+ /** Last seen model identifier */
22
+ lastModel: string | null;
23
+ }
24
+ /**
25
+ * Parse a Codex CLI JSONL rollout file incrementally from a byte offset.
26
+ *
27
+ * Extracts token deltas from `event_msg` lines with `payload.type === "token_count"`.
28
+ * Uses cumulative `total_token_usage` with diff strategy.
29
+ * Tracks model from `turn_context` and `session_meta` events.
30
+ */
31
+ export declare function parseCodexFile(opts: {
32
+ filePath: string;
33
+ startOffset: number;
34
+ lastTotals: TokenDelta | null;
35
+ lastModel: string | null;
36
+ }): Promise<CodexFileResult>;
37
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/parsers/codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,iCAAiC;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA0CD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GAAG,OAAO,CAAC,eAAe,CAAC,CAoF3B"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Codex CLI token parser.
3
+ *
4
+ * Parses Codex JSONL rollout files (~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl)
5
+ * incrementally from a byte offset.
6
+ *
7
+ * Strategy: Cumulative total_token_usage with diff (like Gemini).
8
+ * Each `event_msg` with `payload.type === "token_count"` contains running totals.
9
+ * We diff consecutive totals to produce per-turn deltas.
10
+ *
11
+ * Model is tracked from `turn_context.payload.model` or `session_meta.payload.model`.
12
+ */
13
+ import { createReadStream } from "node:fs";
14
+ import { stat } from "node:fs/promises";
15
+ import { createInterface } from "node:readline";
16
+ /** Check if a TokenDelta is all zeros */
17
+ function isAllZero(d) {
18
+ return (d.inputTokens === 0 &&
19
+ d.cachedInputTokens === 0 &&
20
+ d.outputTokens === 0 &&
21
+ d.reasoningOutputTokens === 0);
22
+ }
23
+ /** Coerce to non-negative integer */
24
+ function toNonNegInt(v) {
25
+ const n = Number(v);
26
+ if (!Number.isFinite(n) || n < 0)
27
+ return 0;
28
+ return Math.floor(n);
29
+ }
30
+ /**
31
+ * Diff two cumulative TokenDelta values.
32
+ * If any field goes negative (counter reset), treat the new value as absolute.
33
+ */
34
+ function diffTotals(current, previous) {
35
+ const dInput = current.inputTokens - previous.inputTokens;
36
+ const dCached = current.cachedInputTokens - previous.cachedInputTokens;
37
+ const dOutput = current.outputTokens - previous.outputTokens;
38
+ const dReasoning = current.reasoningOutputTokens - previous.reasoningOutputTokens;
39
+ // If any field is negative, assume counter reset — use absolute values
40
+ if (dInput < 0 || dCached < 0 || dOutput < 0 || dReasoning < 0) {
41
+ return { ...current };
42
+ }
43
+ return {
44
+ inputTokens: dInput,
45
+ cachedInputTokens: dCached,
46
+ outputTokens: dOutput,
47
+ reasoningOutputTokens: dReasoning,
48
+ };
49
+ }
50
+ /**
51
+ * Parse a Codex CLI JSONL rollout file incrementally from a byte offset.
52
+ *
53
+ * Extracts token deltas from `event_msg` lines with `payload.type === "token_count"`.
54
+ * Uses cumulative `total_token_usage` with diff strategy.
55
+ * Tracks model from `turn_context` and `session_meta` events.
56
+ */
57
+ export async function parseCodexFile(opts) {
58
+ const { filePath, startOffset } = opts;
59
+ const deltas = [];
60
+ let lastTotals = opts.lastTotals;
61
+ let lastModel = opts.lastModel;
62
+ const st = await stat(filePath).catch(() => null);
63
+ if (!st || !st.isFile())
64
+ return { deltas, endOffset: startOffset, lastTotals, lastModel };
65
+ const endOffset = st.size;
66
+ if (endOffset === 0 || startOffset >= endOffset) {
67
+ return { deltas, endOffset: endOffset === 0 ? 0 : endOffset, lastTotals, lastModel };
68
+ }
69
+ const stream = createReadStream(filePath, {
70
+ encoding: "utf8",
71
+ start: startOffset,
72
+ });
73
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
74
+ try {
75
+ for await (const line of rl) {
76
+ if (!line)
77
+ continue;
78
+ let obj;
79
+ try {
80
+ obj = JSON.parse(line);
81
+ }
82
+ catch {
83
+ continue;
84
+ }
85
+ const type = typeof obj.type === "string" ? obj.type : null;
86
+ const timestamp = typeof obj.timestamp === "string" ? obj.timestamp : null;
87
+ const payload = obj.payload;
88
+ // Track model from session_meta
89
+ if (type === "session_meta" && payload) {
90
+ const model = typeof payload.model === "string" ? payload.model.trim() : null;
91
+ if (model)
92
+ lastModel = model;
93
+ continue;
94
+ }
95
+ // Track model from turn_context (overrides session_meta)
96
+ if (type === "turn_context" && payload) {
97
+ const model = typeof payload.model === "string" ? payload.model.trim() : null;
98
+ if (model)
99
+ lastModel = model;
100
+ continue;
101
+ }
102
+ // Extract token counts from event_msg with type=token_count
103
+ if (type === "event_msg" && payload?.type === "token_count" && timestamp) {
104
+ const info = payload.info;
105
+ if (!info)
106
+ continue;
107
+ const usage = info.total_token_usage;
108
+ if (!usage || typeof usage !== "object")
109
+ continue;
110
+ const currentTotals = {
111
+ inputTokens: toNonNegInt(usage.input_tokens),
112
+ cachedInputTokens: toNonNegInt(usage.cached_input_tokens),
113
+ outputTokens: toNonNegInt(usage.output_tokens),
114
+ reasoningOutputTokens: toNonNegInt(usage.reasoning_output_tokens),
115
+ };
116
+ // Compute delta
117
+ const delta = lastTotals ? diffTotals(currentTotals, lastTotals) : { ...currentTotals };
118
+ lastTotals = currentTotals;
119
+ if (isAllZero(delta))
120
+ continue;
121
+ deltas.push({
122
+ source: "codex",
123
+ model: lastModel || "unknown",
124
+ timestamp,
125
+ tokens: delta,
126
+ });
127
+ }
128
+ }
129
+ }
130
+ finally {
131
+ rl.close();
132
+ stream.destroy();
133
+ }
134
+ return { deltas, endOffset, lastTotals, lastModel };
135
+ }
136
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/parsers/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Gemini CLI session collector.
3
+ *
4
+ * Reads a Gemini session JSON file and extracts session-level metadata.
5
+ * Each file contains one session with a messages[] array.
6
+ */
7
+ import type { SessionSnapshot } from "@pew/core";
8
+ /**
9
+ * Collect session snapshots from a Gemini CLI session JSON file.
10
+ *
11
+ * Gemini stores one JSON file per session with:
12
+ * - sessionId (optional)
13
+ * - projectHash (optional)
14
+ * - messages[]: { type, timestamp, model? }
15
+ *
16
+ * Returns an array of 0 or 1 SessionSnapshot.
17
+ */
18
+ export declare function collectGeminiSessions(filePath: string): Promise<SessionSnapshot[]>;
19
+ //# sourceMappingURL=gemini-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-session.d.ts","sourceRoot":"","sources":["../../src/parsers/gemini-session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAU,MAAM,WAAW,CAAC;AAEzD;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,EAAE,CAAC,CA8F5B"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Gemini CLI session collector.
3
+ *
4
+ * Reads a Gemini session JSON file and extracts session-level metadata.
5
+ * Each file contains one session with a messages[] array.
6
+ */
7
+ import { readFile } from "node:fs/promises";
8
+ import { createHash } from "node:crypto";
9
+ import { resolve } from "node:path";
10
+ /**
11
+ * Collect session snapshots from a Gemini CLI session JSON file.
12
+ *
13
+ * Gemini stores one JSON file per session with:
14
+ * - sessionId (optional)
15
+ * - projectHash (optional)
16
+ * - messages[]: { type, timestamp, model? }
17
+ *
18
+ * Returns an array of 0 or 1 SessionSnapshot.
19
+ */
20
+ export async function collectGeminiSessions(filePath) {
21
+ let raw;
22
+ try {
23
+ raw = await readFile(filePath, "utf8");
24
+ }
25
+ catch {
26
+ return [];
27
+ }
28
+ if (!raw.trim())
29
+ return [];
30
+ let session;
31
+ try {
32
+ session = JSON.parse(raw);
33
+ }
34
+ catch {
35
+ return [];
36
+ }
37
+ const messages = Array.isArray(session?.messages)
38
+ ? session.messages
39
+ : [];
40
+ if (messages.length === 0)
41
+ return [];
42
+ // Derive session key
43
+ const sessionId = typeof session.sessionId === "string" ? session.sessionId : null;
44
+ const sessionKey = sessionId
45
+ ? `gemini:${sessionId}`
46
+ : `gemini:${createHash("sha256").update(resolve(filePath)).digest("hex").slice(0, 16)}`;
47
+ // Extract projectRef
48
+ const projectRef = typeof session.projectHash === "string" ? session.projectHash : null;
49
+ // Count messages and track timestamps/model
50
+ let userMessages = 0;
51
+ let assistantMessages = 0;
52
+ let totalMessages = 0;
53
+ let minTimestamp = null;
54
+ let maxTimestamp = null;
55
+ let lastModel = null;
56
+ for (const msg of messages) {
57
+ if (!msg || typeof msg !== "object")
58
+ continue;
59
+ totalMessages++;
60
+ const type = typeof msg.type === "string" ? msg.type : null;
61
+ if (type === "user") {
62
+ userMessages++;
63
+ }
64
+ else if (type === "gemini") {
65
+ assistantMessages++;
66
+ }
67
+ const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : null;
68
+ if (timestamp) {
69
+ if (!minTimestamp || timestamp < minTimestamp) {
70
+ minTimestamp = timestamp;
71
+ }
72
+ if (!maxTimestamp || timestamp > maxTimestamp) {
73
+ maxTimestamp = timestamp;
74
+ }
75
+ }
76
+ const model = typeof msg.model === "string" ? msg.model.trim() : null;
77
+ if (model) {
78
+ lastModel = model;
79
+ }
80
+ }
81
+ if (!minTimestamp)
82
+ return [];
83
+ const startedAt = minTimestamp;
84
+ const lastMessageAt = maxTimestamp ?? minTimestamp;
85
+ const durationMs = new Date(lastMessageAt).getTime() - new Date(startedAt).getTime();
86
+ return [
87
+ {
88
+ sessionKey,
89
+ source: "gemini-cli",
90
+ kind: "human",
91
+ startedAt,
92
+ lastMessageAt,
93
+ durationSeconds: Math.max(0, Math.floor(durationMs / 1000)),
94
+ userMessages,
95
+ assistantMessages,
96
+ totalMessages,
97
+ projectRef,
98
+ model: lastModel,
99
+ snapshotAt: new Date().toISOString(),
100
+ },
101
+ ];
102
+ }
103
+ //# sourceMappingURL=gemini-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-session.js","sourceRoot":"","sources":["../../src/parsers/gemini-session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAAgB;IAEhB,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAE3B,IAAI,OAAgC,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC;QAC/C,CAAC,CAAE,OAAO,CAAC,QAAsC;QACjD,CAAC,CAAC,EAAE,CAAC;IAEP,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,qBAAqB;IACrB,MAAM,SAAS,GACb,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,MAAM,UAAU,GAAG,SAAS;QAC1B,CAAC,CAAC,UAAU,SAAS,EAAE;QACvB,CAAC,CAAC,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAE1F,qBAAqB;IACrB,MAAM,UAAU,GACd,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvE,4CAA4C;IAC5C,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,SAAS,GAAkB,IAAI,CAAC;IAEpC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAS;QAE9C,aAAa,EAAE,CAAC;QAEhB,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5D,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,YAAY,EAAE,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,iBAAiB,EAAE,CAAC;QACtB,CAAC;QAED,MAAM,SAAS,GACb,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3D,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,YAAY,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;gBAC9C,YAAY,GAAG,SAAS,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;gBAC9C,YAAY,GAAG,SAAS,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACtE,IAAI,KAAK,EAAE,CAAC;YACV,SAAS,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,CAAC;IAE7B,MAAM,SAAS,GAAG,YAAY,CAAC;IAC/B,MAAM,aAAa,GAAG,YAAY,IAAI,YAAY,CAAC;IACnD,MAAM,UAAU,GACd,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAEpE,OAAO;QACL;YACE,UAAU;YACV,MAAM,EAAE,YAAsB;YAC9B,IAAI,EAAE,OAAO;YACb,SAAS;YACT,aAAa;YACb,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;YAC3D,YAAY;YACZ,iBAAiB;YACjB,aAAa;YACb,UAAU;YACV,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC;KACF,CAAC;AACJ,CAAC"}