cngkit 1.1.20 → 1.1.22
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/LICENSE +198 -4
- package/README.md +8 -12
- package/dist/{chunk-NGEWD4BW.js → chunk-25Q463MH.js} +1 -2
- package/dist/{chunk-SMTQ3W3F.js → chunk-3BATDTKU.js} +131 -112
- package/dist/chunk-3BATDTKU.js.map +1 -0
- package/dist/chunk-AS7FIJWP.js +88 -0
- package/dist/chunk-AS7FIJWP.js.map +1 -0
- package/dist/chunk-BRFWVQI4.js +18 -0
- package/dist/chunk-BRFWVQI4.js.map +1 -0
- package/dist/{chunk-WDI43VPW.js → chunk-DCW4RKLC.js} +63 -234
- package/dist/chunk-DCW4RKLC.js.map +1 -0
- package/dist/{chunk-SKK2XLRZ.js → chunk-JNHW72SU.js} +21 -15
- package/dist/chunk-JNHW72SU.js.map +1 -0
- package/dist/{chunk-E2GLGGKO.js → chunk-KURN5BJ6.js} +300 -143
- package/dist/chunk-KURN5BJ6.js.map +1 -0
- package/dist/{chunk-CIZBVLN5.js → chunk-N255PYL7.js} +2 -2
- package/dist/chunk-N255PYL7.js.map +1 -0
- package/dist/chunk-NLAANOBW.js +67 -0
- package/dist/chunk-NLAANOBW.js.map +1 -0
- package/dist/chunk-TIY4RTXC.js +268 -0
- package/dist/chunk-TIY4RTXC.js.map +1 -0
- package/dist/chunk-VMTXY4KQ.js +111 -0
- package/dist/chunk-VMTXY4KQ.js.map +1 -0
- package/dist/chunk-YALWTRIP.js +102 -0
- package/dist/chunk-YALWTRIP.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/coderoom/index.js +4 -4
- package/dist/commands/coderoom/join.js +5 -5
- package/dist/commands/coderoom/share.js +8 -11
- package/dist/commands/coderoom/share.js.map +1 -1
- package/dist/commands/hookify/index.js +4 -4
- package/dist/commands/hookify/ingest.js +4 -4
- package/dist/commands/hooks/index.js +4 -4
- package/dist/commands/hooks/install.js +4 -4
- package/dist/commands/hooks/uninstall.js +4 -4
- package/dist/commands/index.js +3 -3
- package/dist/commands/knowledges/audiences.js +26 -8
- package/dist/commands/knowledges/audiences.js.map +1 -1
- package/dist/commands/knowledges/cat.js +26 -7
- package/dist/commands/knowledges/cat.js.map +1 -1
- package/dist/commands/knowledges/files.js +39 -8
- package/dist/commands/knowledges/files.js.map +1 -1
- package/dist/commands/knowledges/find.js +48 -8
- package/dist/commands/knowledges/find.js.map +1 -1
- package/dist/commands/knowledges/glob.js +41 -8
- package/dist/commands/knowledges/glob.js.map +1 -1
- package/dist/commands/knowledges/grep.js +73 -8
- package/dist/commands/knowledges/grep.js.map +1 -1
- package/dist/commands/knowledges/head.js +31 -8
- package/dist/commands/knowledges/head.js.map +1 -1
- package/dist/commands/knowledges/index.js +4 -4
- package/dist/commands/knowledges/list.js +39 -8
- package/dist/commands/knowledges/list.js.map +1 -1
- package/dist/commands/knowledges/ls.js +47 -8
- package/dist/commands/knowledges/ls.js.map +1 -1
- package/dist/commands/knowledges/read.js +46 -8
- package/dist/commands/knowledges/read.js.map +1 -1
- package/dist/commands/knowledges/realpath.js +23 -8
- package/dist/commands/knowledges/realpath.js.map +1 -1
- package/dist/commands/knowledges/search.js +34 -8
- package/dist/commands/knowledges/search.js.map +1 -1
- package/dist/commands/knowledges/stat.js +32 -8
- package/dist/commands/knowledges/stat.js.map +1 -1
- package/dist/commands/knowledges/status.js +26 -8
- package/dist/commands/knowledges/status.js.map +1 -1
- package/dist/commands/knowledges/tail.js +30 -8
- package/dist/commands/knowledges/tail.js.map +1 -1
- package/dist/commands/knowledges/tree.js +38 -8
- package/dist/commands/knowledges/tree.js.map +1 -1
- package/dist/commands/login.js +3 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/scrub.js +3 -3
- package/dist/commands/scrub.js.map +1 -1
- package/dist/commands/transcripts.js +137 -119
- package/dist/commands/transcripts.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-CIZBVLN5.js.map +0 -1
- package/dist/chunk-E2GLGGKO.js.map +0 -1
- package/dist/chunk-MRXGD6TC.js +0 -42
- package/dist/chunk-MRXGD6TC.js.map +0 -1
- package/dist/chunk-NODJM6SH.js +0 -658
- package/dist/chunk-NODJM6SH.js.map +0 -1
- package/dist/chunk-SKK2XLRZ.js.map +0 -1
- package/dist/chunk-SMTQ3W3F.js.map +0 -1
- package/dist/chunk-WDI43VPW.js.map +0 -1
- /package/dist/{chunk-NGEWD4BW.js.map → chunk-25Q463MH.js.map} +0 -0
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
} from "../chunk-XQGLUQFM.js";
|
|
8
8
|
import {
|
|
9
9
|
renderCngkitHelp
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-JNHW72SU.js";
|
|
11
11
|
import {
|
|
12
12
|
GlobalOptionsSchema,
|
|
13
13
|
TranscriptArgsSchema
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-25Q463MH.js";
|
|
15
15
|
import {
|
|
16
16
|
CommandRunner
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-TIY4RTXC.js";
|
|
18
|
+
import "../chunk-DCW4RKLC.js";
|
|
19
19
|
import "../chunk-PZ5AY32C.js";
|
|
20
20
|
|
|
21
21
|
// src/commands/transcripts.tsx
|
|
@@ -26,53 +26,88 @@ import { z } from "zod";
|
|
|
26
26
|
import { createElement } from "react";
|
|
27
27
|
|
|
28
28
|
// src/features/transcripts/reader.ts
|
|
29
|
-
import { promises as
|
|
29
|
+
import { promises as fs3 } from "fs";
|
|
30
|
+
|
|
31
|
+
// src/features/transcripts/jsonl.ts
|
|
32
|
+
import { promises as fs2 } from "fs";
|
|
33
|
+
import path2 from "path";
|
|
34
|
+
|
|
35
|
+
// src/features/transcripts/paths.ts
|
|
30
36
|
import os from "os";
|
|
37
|
+
import { promises as fs } from "fs";
|
|
31
38
|
import path from "path";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
async function statIfExists(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
return await fs.stat(filePath);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
37
48
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const entries = await readEntriesFromFile(file, options2.includeInternal);
|
|
41
|
-
return entries.slice(Math.max(0, entries.length - options2.limit));
|
|
49
|
+
function isNodeError(error) {
|
|
50
|
+
return error instanceof Error && "code" in error;
|
|
42
51
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const matches = [];
|
|
47
|
-
for (const file of files.slice(0, options2.fileLimit)) {
|
|
48
|
-
const entries = await readEntriesFromFile(file, options2.includeInternal);
|
|
49
|
-
for (const entry of entries) {
|
|
50
|
-
if (entry.text.toLowerCase().includes(normalizedQuery)) {
|
|
51
|
-
matches.push(entry);
|
|
52
|
-
}
|
|
53
|
-
if (matches.length >= options2.limit) {
|
|
54
|
-
return matches;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
52
|
+
function expandHome(value) {
|
|
53
|
+
if (value === "~") {
|
|
54
|
+
return os.homedir();
|
|
57
55
|
}
|
|
58
|
-
|
|
56
|
+
if (value.startsWith("~/")) {
|
|
57
|
+
return path.join(os.homedir(), value.slice(2));
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));
|
|
63
|
-
return records.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
61
|
+
function detectSourceFromPath(filePath) {
|
|
62
|
+
return filePath.includes(`${path.sep}.claude${path.sep}`) ? "claude" : "codex";
|
|
64
63
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
function truncateSingleLine(value, maxLength) {
|
|
65
|
+
const line = value.replace(/\s+/g, " ").trim();
|
|
66
|
+
return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/features/transcripts/jsonl.ts
|
|
70
|
+
async function readJsonlLines(filePath, limit) {
|
|
71
|
+
const content = await fs2.readFile(filePath, "utf8");
|
|
72
|
+
return content.split("\n").filter(Boolean).slice(0, limit);
|
|
73
|
+
}
|
|
74
|
+
function parseJsonObject(line) {
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(line);
|
|
77
|
+
return asObject(parsed);
|
|
78
|
+
} catch {
|
|
79
|
+
return void 0;
|
|
71
80
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
}
|
|
82
|
+
function asObject(value) {
|
|
83
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
function readString(value) {
|
|
89
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
90
|
+
}
|
|
91
|
+
function extractContentText(value) {
|
|
92
|
+
if (typeof value === "string") {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
if (!Array.isArray(value)) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
const parts = value.flatMap((item) => {
|
|
99
|
+
if (typeof item === "string") {
|
|
100
|
+
return [item];
|
|
101
|
+
}
|
|
102
|
+
const objectItem = asObject(item);
|
|
103
|
+
if (!objectItem) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
return [readString(objectItem.text), readString(objectItem.content)].filter(
|
|
107
|
+
(part) => Boolean(part)
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
76
111
|
}
|
|
77
112
|
async function discoverJsonlFiles(source, rootPath) {
|
|
78
113
|
const stat = await statIfExists(rootPath);
|
|
@@ -83,15 +118,15 @@ async function discoverJsonlFiles(source, rootPath) {
|
|
|
83
118
|
return [await recordFromPath(source, rootPath, stat.mtime)];
|
|
84
119
|
}
|
|
85
120
|
const records = [];
|
|
86
|
-
const entries = await
|
|
121
|
+
const entries = await fs2.readdir(rootPath, { withFileTypes: true });
|
|
87
122
|
for (const entry of entries) {
|
|
88
|
-
const entryPath =
|
|
123
|
+
const entryPath = path2.join(rootPath, entry.name);
|
|
89
124
|
if (entry.isDirectory()) {
|
|
90
125
|
records.push(...await discoverJsonlFiles(source, entryPath));
|
|
91
126
|
continue;
|
|
92
127
|
}
|
|
93
128
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
94
|
-
const entryStat = await
|
|
129
|
+
const entryStat = await fs2.stat(entryPath);
|
|
95
130
|
records.push(await recordFromPath(source, entryPath, entryStat.mtime));
|
|
96
131
|
}
|
|
97
132
|
}
|
|
@@ -104,6 +139,31 @@ async function recordFromPath(source, filePath, mtime) {
|
|
|
104
139
|
updatedAt: mtime.toISOString()
|
|
105
140
|
};
|
|
106
141
|
}
|
|
142
|
+
|
|
143
|
+
// src/features/transcripts/discovery.ts
|
|
144
|
+
import path3 from "path";
|
|
145
|
+
|
|
146
|
+
// src/features/transcripts/types.ts
|
|
147
|
+
var TranscriptSourceNames = ["codex", "claude"];
|
|
148
|
+
|
|
149
|
+
// src/features/transcripts/discovery.ts
|
|
150
|
+
async function discoverTranscriptFiles(source) {
|
|
151
|
+
const sources = source === "all" ? TranscriptSourceNames : [source];
|
|
152
|
+
const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));
|
|
153
|
+
return records.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
154
|
+
}
|
|
155
|
+
function sourceRoots(source) {
|
|
156
|
+
if (source === "codex") {
|
|
157
|
+
return [
|
|
158
|
+
discoverJsonlFiles("codex", expandHome("~/.codex/sessions")),
|
|
159
|
+
discoverJsonlFiles("codex", expandHome("~/.codex/archived_sessions"))
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
return [
|
|
163
|
+
discoverJsonlFiles("claude", expandHome("~/.claude/projects")),
|
|
164
|
+
discoverJsonlFiles("claude", expandHome("~/.claude/history.jsonl"))
|
|
165
|
+
];
|
|
166
|
+
}
|
|
107
167
|
async function readTranscriptRecord(record) {
|
|
108
168
|
const metadata = await readMetadataFromFile(record.source, record.filePath);
|
|
109
169
|
return {
|
|
@@ -142,7 +202,7 @@ async function readMetadataFromFile(source, filePath) {
|
|
|
142
202
|
}
|
|
143
203
|
}
|
|
144
204
|
return {
|
|
145
|
-
sessionId:
|
|
205
|
+
sessionId: path3.basename(filePath, ".jsonl")
|
|
146
206
|
};
|
|
147
207
|
}
|
|
148
208
|
async function resolveTranscriptFile(source, target) {
|
|
@@ -161,8 +221,37 @@ async function resolveTranscriptFile(source, target) {
|
|
|
161
221
|
}
|
|
162
222
|
return match;
|
|
163
223
|
}
|
|
224
|
+
|
|
225
|
+
// src/features/transcripts/reader.ts
|
|
226
|
+
async function listTranscriptRecords(options2) {
|
|
227
|
+
const files = await discoverTranscriptFiles(options2.source);
|
|
228
|
+
const records = await Promise.all(files.slice(0, options2.limit).map(readTranscriptRecord));
|
|
229
|
+
return records;
|
|
230
|
+
}
|
|
231
|
+
async function readTranscriptEntries(options2) {
|
|
232
|
+
const file = await resolveTranscriptFile(options2.source, options2.target);
|
|
233
|
+
const entries = await readEntriesFromFile(file, options2.includeInternal);
|
|
234
|
+
return entries.slice(Math.max(0, entries.length - options2.limit));
|
|
235
|
+
}
|
|
236
|
+
async function grepTranscriptEntries(options2) {
|
|
237
|
+
const files = await discoverTranscriptFiles(options2.source);
|
|
238
|
+
const normalizedQuery = options2.query.toLowerCase();
|
|
239
|
+
const matches = [];
|
|
240
|
+
for (const file of files.slice(0, options2.fileLimit)) {
|
|
241
|
+
const entries = await readEntriesFromFile(file, options2.includeInternal);
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (entry.text.toLowerCase().includes(normalizedQuery)) {
|
|
244
|
+
matches.push(entry);
|
|
245
|
+
}
|
|
246
|
+
if (matches.length >= options2.limit) {
|
|
247
|
+
return matches;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return matches;
|
|
252
|
+
}
|
|
164
253
|
async function readEntriesFromFile(record, includeInternal) {
|
|
165
|
-
const content = await
|
|
254
|
+
const content = await fs3.readFile(record.filePath, "utf8");
|
|
166
255
|
const entries = [];
|
|
167
256
|
for (const line of content.split("\n")) {
|
|
168
257
|
if (!line.trim()) {
|
|
@@ -218,77 +307,6 @@ function parseClaudeEntry(record, parsed, includeInternal) {
|
|
|
218
307
|
text
|
|
219
308
|
};
|
|
220
309
|
}
|
|
221
|
-
function extractContentText(value) {
|
|
222
|
-
if (typeof value === "string") {
|
|
223
|
-
return value;
|
|
224
|
-
}
|
|
225
|
-
if (!Array.isArray(value)) {
|
|
226
|
-
return void 0;
|
|
227
|
-
}
|
|
228
|
-
const parts = value.flatMap((item) => {
|
|
229
|
-
if (typeof item === "string") {
|
|
230
|
-
return [item];
|
|
231
|
-
}
|
|
232
|
-
const objectItem = asObject(item);
|
|
233
|
-
if (!objectItem) {
|
|
234
|
-
return [];
|
|
235
|
-
}
|
|
236
|
-
return [readString(objectItem.text), readString(objectItem.content)].filter(
|
|
237
|
-
(part) => Boolean(part)
|
|
238
|
-
);
|
|
239
|
-
});
|
|
240
|
-
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
241
|
-
}
|
|
242
|
-
async function readJsonlLines(filePath, limit) {
|
|
243
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
244
|
-
return content.split("\n").filter(Boolean).slice(0, limit);
|
|
245
|
-
}
|
|
246
|
-
async function statIfExists(filePath) {
|
|
247
|
-
try {
|
|
248
|
-
return await fs.stat(filePath);
|
|
249
|
-
} catch (error) {
|
|
250
|
-
if (isNodeError(error) && error.code === "ENOENT") {
|
|
251
|
-
return void 0;
|
|
252
|
-
}
|
|
253
|
-
throw error;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
function parseJsonObject(line) {
|
|
257
|
-
try {
|
|
258
|
-
const parsed = JSON.parse(line);
|
|
259
|
-
return asObject(parsed);
|
|
260
|
-
} catch {
|
|
261
|
-
return void 0;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
function asObject(value) {
|
|
265
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
266
|
-
return value;
|
|
267
|
-
}
|
|
268
|
-
return void 0;
|
|
269
|
-
}
|
|
270
|
-
function readString(value) {
|
|
271
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
272
|
-
}
|
|
273
|
-
function isNodeError(error) {
|
|
274
|
-
return error instanceof Error && "code" in error;
|
|
275
|
-
}
|
|
276
|
-
function expandHome(value) {
|
|
277
|
-
if (value === "~") {
|
|
278
|
-
return os.homedir();
|
|
279
|
-
}
|
|
280
|
-
if (value.startsWith("~/")) {
|
|
281
|
-
return path.join(os.homedir(), value.slice(2));
|
|
282
|
-
}
|
|
283
|
-
return value;
|
|
284
|
-
}
|
|
285
|
-
function detectSourceFromPath(filePath) {
|
|
286
|
-
return filePath.includes(`${path.sep}.claude${path.sep}`) ? "claude" : "codex";
|
|
287
|
-
}
|
|
288
|
-
function truncateSingleLine(value, maxLength) {
|
|
289
|
-
const line = value.replace(/\s+/g, " ").trim();
|
|
290
|
-
return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;
|
|
291
|
-
}
|
|
292
310
|
|
|
293
311
|
// src/features/transcripts/transcript-output.tsx
|
|
294
312
|
import { Box, Text } from "ink";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/transcripts.tsx","../../src/features/transcripts/run-transcript-command.ts","../../src/features/transcripts/reader.ts","../../src/features/transcripts/transcript-output.tsx"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { GlobalOptionsSchema, TranscriptArgsSchema } from \"../cli/options.js\";\nimport { runTranscriptCommand, type TranscriptCommandOptions } from \"../features/transcripts/run-transcript-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"List, read, and grep local Claude/Codex transcript JSONL files\";\nexport const args = TranscriptArgsSchema;\nexport const options = GlobalOptionsSchema.extend({\n source: z\n .enum([\"all\", \"codex\", \"claude\"])\n .optional()\n .describe(\n option({\n description: \"Transcript source. Default: all\",\n valueDescription: \"all|codex|claude\",\n })\n ),\n limit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum records or entries\",\n valueDescription: \"n\",\n })\n ),\n fileLimit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum recent files to scan for grep\",\n valueDescription: \"n\",\n })\n ),\n includeInternal: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Include developer/system/internal transcript entries\",\n })\n ),\n json: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Print raw JSON\",\n })\n ),\n});\n\ntype TranscriptsCommandProps = {\n readonly args: string[];\n readonly options: TranscriptCommandOptions;\n};\n\nexport default function TranscriptsCommand({ args, options }: TranscriptsCommandProps) {\n return <CommandRunner run={(output) => runTranscriptCommand(args, options, output)} />;\n}\n","import { createElement } from \"react\";\n\nimport { renderCngkitHelp } from \"../../cli/help-specs.js\";\nimport {\n coerceLimit,\n formatJson,\n optionalJoinedArgument,\n shouldPrintJson,\n type JsonOutputOptions,\n type NumberOption,\n} from \"../../shared/command-utils.js\";\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output.js\";\nimport {\n grepTranscriptEntries,\n listTranscriptRecords,\n readTranscriptEntries,\n type TranscriptEntry,\n type TranscriptScopeName,\n} from \"./reader.js\";\nimport { TranscriptRecordList } from \"./transcript-output.js\";\n\nexport type TranscriptCommandOptions = GlobalCommandOptions &\n JsonOutputOptions & {\n source?: string;\n limit?: NumberOption;\n fileLimit?: NumberOption;\n includeInternal?: boolean;\n };\n\nexport async function runTranscriptCommand(\n args: string[] | undefined,\n options: TranscriptCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const [action = \"list\", ...actionArgs] = args ?? [];\n const source = normalizeTranscriptSource(options.source);\n const limit = coerceLimit(options.limit, action === \"list\" ? 12 : 80, 500);\n const includeInternal = options.includeInternal === true;\n\n switch (action) {\n case \"help\":\n output.component(renderCngkitHelp(\"transcripts\"));\n return;\n case \"list\": {\n const records = await listTranscriptRecords({ source, limit });\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ records }));\n return;\n }\n\n output.component(createElement(TranscriptRecordList, { records }));\n return;\n }\n case \"read\": {\n const target = actionArgs[0];\n if (!target) {\n throw new Error(\"Missing transcript target. Usage: cngkit transcripts read <path-or-id>\");\n }\n\n const entries = await readTranscriptEntries({\n source,\n target,\n limit,\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n case \"grep\": {\n const query = optionalJoinedArgument(actionArgs);\n if (!query) {\n throw new Error(\"Missing query. Usage: cngkit transcripts grep <query>\");\n }\n\n const entries = await grepTranscriptEntries({\n source,\n query,\n limit,\n fileLimit: coerceLimit(options.fileLimit, 60, 5000),\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n default:\n throw new Error(\"Unknown transcripts command. Usage: cngkit transcripts <list|read|grep>\");\n }\n}\n\nfunction formatTranscriptEntries(entries: TranscriptEntry[]): string {\n if (entries.length === 0) {\n return \"No transcript entries found.\";\n }\n\n return entries\n .map((entry) => {\n const when = entry.timestamp ? ` ${entry.timestamp}` : \"\";\n return `[${entry.source}] ${entry.role}${when}\\n${entry.text.trim()}`;\n })\n .join(\"\\n\\n\");\n}\n\nfunction normalizeTranscriptSource(value: string | undefined): TranscriptScopeName {\n if (value === undefined) {\n return \"all\";\n }\n\n switch (value) {\n case \"all\":\n case \"codex\":\n case \"claude\":\n return value;\n default:\n throw new Error(\"Unknown transcript source. Use one of: all, codex, claude.\");\n }\n}\n","import { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport const TranscriptSourceNames = [\"codex\", \"claude\"] as const;\n\nexport type TranscriptSourceName = (typeof TranscriptSourceNames)[number];\nexport type TranscriptScopeName = TranscriptSourceName | \"all\";\n\nexport type TranscriptRecord = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly updatedAt: string;\n readonly sessionId?: string;\n readonly cwd?: string;\n readonly title?: string;\n};\n\nexport type TranscriptEntry = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly timestamp?: string;\n readonly role: string;\n readonly text: string;\n};\n\nexport type TranscriptListOptions = {\n readonly source: TranscriptScopeName;\n readonly limit: number;\n};\n\nexport type TranscriptReadOptions = {\n readonly source: TranscriptScopeName;\n readonly target: string;\n readonly limit: number;\n readonly includeInternal: boolean;\n};\n\nexport type TranscriptGrepOptions = {\n readonly source: TranscriptScopeName;\n readonly query: string;\n readonly limit: number;\n readonly fileLimit: number;\n readonly includeInternal: boolean;\n};\n\ntype JsonObject = Record<string, unknown>;\n\nexport async function listTranscriptRecords(\n options: TranscriptListOptions\n): Promise<TranscriptRecord[]> {\n const files = await discoverTranscriptFiles(options.source);\n const records = await Promise.all(files.slice(0, options.limit).map(readTranscriptRecord));\n return records;\n}\n\nexport async function readTranscriptEntries(\n options: TranscriptReadOptions\n): Promise<TranscriptEntry[]> {\n const file = await resolveTranscriptFile(options.source, options.target);\n const entries = await readEntriesFromFile(file, options.includeInternal);\n return entries.slice(Math.max(0, entries.length - options.limit));\n}\n\nexport async function grepTranscriptEntries(\n options: TranscriptGrepOptions\n): Promise<TranscriptEntry[]> {\n const files = await discoverTranscriptFiles(options.source);\n const normalizedQuery = options.query.toLowerCase();\n const matches: TranscriptEntry[] = [];\n\n for (const file of files.slice(0, options.fileLimit)) {\n const entries = await readEntriesFromFile(file, options.includeInternal);\n\n for (const entry of entries) {\n if (entry.text.toLowerCase().includes(normalizedQuery)) {\n matches.push(entry);\n }\n\n if (matches.length >= options.limit) {\n return matches;\n }\n }\n }\n\n return matches;\n}\n\nasync function discoverTranscriptFiles(source: TranscriptScopeName): Promise<TranscriptRecord[]> {\n const sources = source === \"all\" ? TranscriptSourceNames : [source];\n const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));\n return records\n .flat()\n .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));\n}\n\nfunction sourceRoots(source: TranscriptSourceName): Promise<TranscriptRecord[]>[] {\n if (source === \"codex\") {\n return [\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/sessions\")),\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/archived_sessions\")),\n ];\n }\n\n return [\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/projects\")),\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/history.jsonl\")),\n ];\n}\n\nasync function discoverJsonlFiles(\n source: TranscriptSourceName,\n rootPath: string\n): Promise<TranscriptRecord[]> {\n const stat = await statIfExists(rootPath);\n if (!stat) {\n return [];\n }\n\n if (stat.isFile()) {\n return [await recordFromPath(source, rootPath, stat.mtime)];\n }\n\n const records: TranscriptRecord[] = [];\n const entries = await fs.readdir(rootPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const entryPath = path.join(rootPath, entry.name);\n if (entry.isDirectory()) {\n records.push(...(await discoverJsonlFiles(source, entryPath)));\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n const entryStat = await fs.stat(entryPath);\n records.push(await recordFromPath(source, entryPath, entryStat.mtime));\n }\n }\n\n return records;\n}\n\nasync function recordFromPath(\n source: TranscriptSourceName,\n filePath: string,\n mtime: Date\n): Promise<TranscriptRecord> {\n return {\n source,\n filePath,\n updatedAt: mtime.toISOString(),\n };\n}\n\nasync function readTranscriptRecord(record: TranscriptRecord): Promise<TranscriptRecord> {\n const metadata = await readMetadataFromFile(record.source, record.filePath);\n return {\n ...record,\n ...metadata,\n };\n}\n\nasync function readMetadataFromFile(\n source: TranscriptSourceName,\n filePath: string\n): Promise<Pick<TranscriptRecord, \"cwd\" | \"sessionId\" | \"title\">> {\n const lines = await readJsonlLines(filePath, 80);\n\n for (const line of lines) {\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n if (source === \"codex\") {\n const payload = asObject(parsed.payload);\n if (parsed.type === \"session_meta\" && payload) {\n return {\n cwd: readString(payload.cwd),\n sessionId: readString(payload.session_id) ?? readString(payload.id),\n title: readString(payload.cwd),\n };\n }\n }\n\n if (source === \"claude\") {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n const content = message ? extractContentText(message.content) : readString(parsed.content);\n if (content && (role === \"user\" || role === \"assistant\")) {\n return {\n cwd: readString(parsed.cwd) ?? readString(parsed.project),\n sessionId: readString(parsed.sessionId),\n title: truncateSingleLine(content, 120),\n };\n }\n }\n }\n\n return {\n sessionId: path.basename(filePath, \".jsonl\"),\n };\n}\n\nasync function resolveTranscriptFile(\n source: TranscriptScopeName,\n target: string\n): Promise<TranscriptRecord> {\n const expandedTarget = expandHome(target);\n const directStat = await statIfExists(expandedTarget);\n if (directStat?.isFile()) {\n return recordFromPath(detectSourceFromPath(expandedTarget), expandedTarget, directStat.mtime);\n }\n\n const files = await discoverTranscriptFiles(source);\n const normalizedTarget = target.toLowerCase();\n const match = files.find((file) => {\n return (\n file.filePath.toLowerCase().includes(normalizedTarget) ||\n file.sessionId?.toLowerCase().includes(normalizedTarget)\n );\n });\n\n if (!match) {\n throw new Error(`No transcript matched \"${target}\". Run cngkit transcripts list first.`);\n }\n\n return match;\n}\n\nasync function readEntriesFromFile(\n record: TranscriptRecord,\n includeInternal: boolean\n): Promise<TranscriptEntry[]> {\n const content = await fs.readFile(record.filePath, \"utf8\");\n const entries: TranscriptEntry[] = [];\n\n for (const line of content.split(\"\\n\")) {\n if (!line.trim()) {\n continue;\n }\n\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n const entry =\n record.source === \"codex\"\n ? parseCodexEntry(record, parsed, includeInternal)\n : parseClaudeEntry(record, parsed, includeInternal);\n\n if (entry) {\n entries.push(entry);\n }\n }\n\n return entries;\n}\n\nfunction parseCodexEntry(\n record: TranscriptRecord,\n parsed: JsonObject,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const payload = asObject(parsed.payload);\n if (!payload || parsed.type !== \"response_item\") {\n return undefined;\n }\n\n const role = readString(payload.role);\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = extractContentText(payload.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n\nfunction parseClaudeEntry(\n record: TranscriptRecord,\n parsed: JsonObject,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = message ? extractContentText(message.content) : readString(parsed.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n\nfunction extractContentText(value: unknown): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const parts = value.flatMap((item) => {\n if (typeof item === \"string\") {\n return [item];\n }\n\n const objectItem = asObject(item);\n if (!objectItem) {\n return [];\n }\n\n return [readString(objectItem.text), readString(objectItem.content)].filter(\n (part): part is string => Boolean(part)\n );\n });\n\n return parts.length > 0 ? parts.join(\"\\n\") : undefined;\n}\n\nasync function readJsonlLines(filePath: string, limit: number): Promise<string[]> {\n const content = await fs.readFile(filePath, \"utf8\");\n return content.split(\"\\n\").filter(Boolean).slice(0, limit);\n}\n\nasync function statIfExists(filePath: string) {\n try {\n return await fs.stat(filePath);\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nfunction parseJsonObject(line: string): JsonObject | undefined {\n try {\n const parsed: unknown = JSON.parse(line);\n return asObject(parsed);\n } catch {\n return undefined;\n }\n}\n\nfunction asObject(value: unknown): JsonObject | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as JsonObject;\n }\n return undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nfunction expandHome(value: string): string {\n if (value === \"~\") {\n return os.homedir();\n }\n if (value.startsWith(\"~/\")) {\n return path.join(os.homedir(), value.slice(2));\n }\n return value;\n}\n\nfunction detectSourceFromPath(filePath: string): TranscriptSourceName {\n return filePath.includes(`${path.sep}.claude${path.sep}`) ? \"claude\" : \"codex\";\n}\n\nfunction truncateSingleLine(value: string, maxLength: number): string {\n const line = value.replace(/\\s+/g, \" \").trim();\n return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;\n}\n","import { Box, Text } from \"ink\";\n\nimport { singleLine } from \"../../shared/command-utils.js\";\nimport type { TranscriptRecord } from \"./reader.js\";\n\nexport function TranscriptRecordList({ records }: { readonly records: readonly TranscriptRecord[] }) {\n if (records.length === 0) {\n return <Text color=\"yellow\">No transcript files found.</Text>;\n }\n\n return (\n <Box flexDirection=\"column\">\n {records.map((record, index) => (\n <Box flexDirection=\"column\" key={`${record.source}-${record.filePath}-${index}`} marginBottom={1}>\n <Text>\n <Text color=\"cyan\">{index + 1}. </Text>\n <Text bold>{record.source}</Text>\n <Text dimColor> {record.updatedAt}</Text>\n </Text>\n <Text>{record.filePath}</Text>\n {record.sessionId ? (\n <Text dimColor>\n session {record.sessionId}\n </Text>\n ) : null}\n {record.cwd ? <Text dimColor>cwd {record.cwd}</Text> : null}\n {record.title ? <Text>{singleLine(record.title)}</Text> : null}\n </Box>\n ))}\n </Box>\n );\n}\n"],"mappings":"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","names":["options","args","options","jsx","args","options"]}
|
|
1
|
+
{"version":3,"sources":["../../src/commands/transcripts.tsx","../../src/features/transcripts/run-transcript-command.ts","../../src/features/transcripts/reader.ts","../../src/features/transcripts/jsonl.ts","../../src/features/transcripts/paths.ts","../../src/features/transcripts/discovery.ts","../../src/features/transcripts/types.ts","../../src/features/transcripts/transcript-output.tsx"],"sourcesContent":["import { option } from \"pastel\";\nimport { z } from \"zod\";\n\nimport { GlobalOptionsSchema, TranscriptArgsSchema } from \"../cli/options.js\";\nimport { runTranscriptCommand, type TranscriptCommandOptions } from \"../features/transcripts/run-transcript-command.js\";\nimport { CommandRunner } from \"../cli/command-runner.js\";\n\nexport const description = \"List, read, and grep local Claude/Codex transcript JSONL files\";\nexport const args = TranscriptArgsSchema;\nexport const options = GlobalOptionsSchema.extend({\n source: z\n .enum([\"all\", \"codex\", \"claude\"])\n .optional()\n .describe(\n option({\n description: \"Transcript source. Default: all\",\n valueDescription: \"all|codex|claude\",\n })\n ),\n limit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum records or entries\",\n valueDescription: \"n\",\n })\n ),\n fileLimit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum recent files to scan for grep\",\n valueDescription: \"n\",\n })\n ),\n includeInternal: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Include developer/system/internal transcript entries\",\n })\n ),\n json: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Print raw JSON\",\n })\n ),\n});\n\ntype TranscriptsCommandProps = {\n readonly args: string[];\n readonly options: TranscriptCommandOptions;\n};\n\nexport default function TranscriptsCommand({ args, options }: TranscriptsCommandProps) {\n return <CommandRunner run={(output) => runTranscriptCommand(args, options, output)} />;\n}\n","import { createElement } from \"react\";\n\nimport { renderCngkitHelp } from \"../../cli/help-specs.js\";\nimport {\n coerceLimit,\n formatJson,\n optionalJoinedArgument,\n shouldPrintJson,\n type JsonOutputOptions,\n type NumberOption,\n} from \"../../shared/command-utils.js\";\nimport type { GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output/types.js\";\nimport {\n grepTranscriptEntries,\n listTranscriptRecords,\n readTranscriptEntries,\n type TranscriptEntry,\n type TranscriptScopeName,\n} from \"./reader.js\";\nimport { TranscriptRecordList } from \"./transcript-output.js\";\n\nexport type TranscriptCommandOptions = GlobalCommandOptions &\n JsonOutputOptions & {\n source?: string;\n limit?: NumberOption;\n fileLimit?: NumberOption;\n includeInternal?: boolean;\n };\n\nexport async function runTranscriptCommand(\n args: string[] | undefined,\n options: TranscriptCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const [action = \"list\", ...actionArgs] = args ?? [];\n const source = normalizeTranscriptSource(options.source);\n const limit = coerceLimit(options.limit, action === \"list\" ? 12 : 80, 500);\n const includeInternal = options.includeInternal === true;\n\n switch (action) {\n case \"help\":\n output.component(renderCngkitHelp(\"transcripts\"));\n return;\n case \"list\": {\n const records = await listTranscriptRecords({ source, limit });\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ records }));\n return;\n }\n\n output.component(createElement(TranscriptRecordList, { records }));\n return;\n }\n case \"read\": {\n const target = actionArgs[0];\n if (!target) {\n throw new Error(\"Missing transcript target. Usage: cngkit transcripts read <path-or-id>\");\n }\n\n const entries = await readTranscriptEntries({\n source,\n target,\n limit,\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n case \"grep\": {\n const query = optionalJoinedArgument(actionArgs);\n if (!query) {\n throw new Error(\"Missing query. Usage: cngkit transcripts grep <query>\");\n }\n\n const entries = await grepTranscriptEntries({\n source,\n query,\n limit,\n fileLimit: coerceLimit(options.fileLimit, 60, 5000),\n includeInternal,\n });\n\n if (shouldPrintJson(options)) {\n output.raw(formatJson({ entries }));\n return;\n }\n\n output.raw(formatTranscriptEntries(entries));\n return;\n }\n default:\n throw new Error(\"Unknown transcripts command. Usage: cngkit transcripts <list|read|grep>\");\n }\n}\n\nfunction formatTranscriptEntries(entries: TranscriptEntry[]): string {\n if (entries.length === 0) {\n return \"No transcript entries found.\";\n }\n\n return entries\n .map((entry) => {\n const when = entry.timestamp ? ` ${entry.timestamp}` : \"\";\n return `[${entry.source}] ${entry.role}${when}\\n${entry.text.trim()}`;\n })\n .join(\"\\n\\n\");\n}\n\nfunction normalizeTranscriptSource(value: string | undefined): TranscriptScopeName {\n if (value === undefined) {\n return \"all\";\n }\n\n switch (value) {\n case \"all\":\n case \"codex\":\n case \"claude\":\n return value;\n default:\n throw new Error(\"Unknown transcript source. Use one of: all, codex, claude.\");\n }\n}\n","// Public API entrypoint for transcript reading. Internal helpers live in\n// `./types.ts`, `./paths.ts`, `./jsonl.ts`, and `./discovery.ts`; only the\n// consumer-facing functions are exported here.\n\nimport { promises as fs } from \"node:fs\";\n\nimport {\n asObject,\n extractContentText,\n parseJsonObject,\n readString,\n} from \"./jsonl.js\";\nimport {\n discoverTranscriptFiles,\n readTranscriptRecord,\n resolveTranscriptFile,\n} from \"./discovery.js\";\nimport type {\n TranscriptEntry,\n TranscriptGrepOptions,\n TranscriptListOptions,\n TranscriptRecord,\n TranscriptReadOptions,\n} from \"./types.js\";\n\nexport {\n TranscriptSourceNames,\n type TranscriptEntry,\n type TranscriptGrepOptions,\n type TranscriptListOptions,\n type TranscriptReadOptions,\n type TranscriptRecord,\n type TranscriptScopeName,\n type TranscriptSourceName,\n} from \"./types.js\";\n\nexport async function listTranscriptRecords(\n options: TranscriptListOptions\n): Promise<TranscriptRecord[]> {\n const files = await discoverTranscriptFiles(options.source);\n const records = await Promise.all(files.slice(0, options.limit).map(readTranscriptRecord));\n return records;\n}\n\nexport async function readTranscriptEntries(\n options: TranscriptReadOptions\n): Promise<TranscriptEntry[]> {\n const file = await resolveTranscriptFile(options.source, options.target);\n const entries = await readEntriesFromFile(file, options.includeInternal);\n return entries.slice(Math.max(0, entries.length - options.limit));\n}\n\nexport async function grepTranscriptEntries(\n options: TranscriptGrepOptions\n): Promise<TranscriptEntry[]> {\n const files = await discoverTranscriptFiles(options.source);\n const normalizedQuery = options.query.toLowerCase();\n const matches: TranscriptEntry[] = [];\n\n for (const file of files.slice(0, options.fileLimit)) {\n const entries = await readEntriesFromFile(file, options.includeInternal);\n\n for (const entry of entries) {\n if (entry.text.toLowerCase().includes(normalizedQuery)) {\n matches.push(entry);\n }\n\n if (matches.length >= options.limit) {\n return matches;\n }\n }\n }\n\n return matches;\n}\n\nasync function readEntriesFromFile(\n record: TranscriptRecord,\n includeInternal: boolean\n): Promise<TranscriptEntry[]> {\n const content = await fs.readFile(record.filePath, \"utf8\");\n const entries: TranscriptEntry[] = [];\n\n for (const line of content.split(\"\\n\")) {\n if (!line.trim()) {\n continue;\n }\n\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n const entry =\n record.source === \"codex\"\n ? parseCodexEntry(record, parsed, includeInternal)\n : parseClaudeEntry(record, parsed, includeInternal);\n\n if (entry) {\n entries.push(entry);\n }\n }\n\n return entries;\n}\n\nfunction parseCodexEntry(\n record: TranscriptRecord,\n parsed: Record<string, unknown>,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const payload = asObject(parsed.payload);\n if (!payload || parsed.type !== \"response_item\") {\n return undefined;\n }\n\n const role = readString(payload.role);\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = extractContentText(payload.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n\nfunction parseClaudeEntry(\n record: TranscriptRecord,\n parsed: Record<string, unknown>,\n includeInternal: boolean\n): TranscriptEntry | undefined {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n\n if (!role || (!includeInternal && role !== \"user\" && role !== \"assistant\")) {\n return undefined;\n }\n\n const text = message ? extractContentText(message.content) : readString(parsed.content);\n if (!text) {\n return undefined;\n }\n\n return {\n source: record.source,\n filePath: record.filePath,\n timestamp: readString(parsed.timestamp),\n role,\n text,\n };\n}\n","import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { TranscriptRecord } from \"./types.js\";\nimport { statIfExists } from \"./paths.js\";\n\nexport async function readJsonlLines(filePath: string, limit: number): Promise<string[]> {\n const content = await fs.readFile(filePath, \"utf8\");\n return content.split(\"\\n\").filter(Boolean).slice(0, limit);\n}\n\nexport function parseJsonObject(line: string): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(line);\n return asObject(parsed);\n } catch {\n return undefined;\n }\n}\n\nexport function asObject(value: unknown): Record<string, unknown> | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nexport function readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nexport function extractContentText(value: unknown): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const parts = value.flatMap((item) => {\n if (typeof item === \"string\") {\n return [item];\n }\n\n const objectItem = asObject(item);\n if (!objectItem) {\n return [];\n }\n\n return [readString(objectItem.text), readString(objectItem.content)].filter(\n (part): part is string => Boolean(part)\n );\n });\n\n return parts.length > 0 ? parts.join(\"\\n\") : undefined;\n}\n\nexport async function discoverJsonlFiles(\n source: TranscriptRecord[\"source\"],\n rootPath: string\n): Promise<TranscriptRecord[]> {\n const stat = await statIfExists(rootPath);\n if (!stat) {\n return [];\n }\n\n if (stat.isFile()) {\n return [await recordFromPath(source, rootPath, stat.mtime)];\n }\n\n const records: TranscriptRecord[] = [];\n const entries = await fs.readdir(rootPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const entryPath = path.join(rootPath, entry.name);\n if (entry.isDirectory()) {\n records.push(...(await discoverJsonlFiles(source, entryPath)));\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n const entryStat = await fs.stat(entryPath);\n records.push(await recordFromPath(source, entryPath, entryStat.mtime));\n }\n }\n\n return records;\n}\n\nexport async function recordFromPath(\n source: TranscriptRecord[\"source\"],\n filePath: string,\n mtime: Date\n): Promise<TranscriptRecord> {\n return {\n source,\n filePath,\n updatedAt: mtime.toISOString(),\n };\n}\n","import os from \"node:os\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { TranscriptSourceName } from \"./types.js\";\n\nexport async function statIfExists(filePath: string) {\n try {\n return await fs.stat(filePath);\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport function isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nexport function expandHome(value: string): string {\n if (value === \"~\") {\n return os.homedir();\n }\n if (value.startsWith(\"~/\")) {\n return path.join(os.homedir(), value.slice(2));\n }\n return value;\n}\n\nexport function detectSourceFromPath(filePath: string): TranscriptSourceName {\n return filePath.includes(`${path.sep}.claude${path.sep}`) ? \"claude\" : \"codex\";\n}\n\nexport function truncateSingleLine(value: string, maxLength: number): string {\n const line = value.replace(/\\s+/g, \" \").trim();\n return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;\n}\n","import path from \"node:path\";\n\nimport {\n asObject,\n discoverJsonlFiles,\n extractContentText,\n parseJsonObject,\n readJsonlLines,\n readString,\n recordFromPath,\n} from \"./jsonl.js\";\nimport {\n detectSourceFromPath,\n expandHome,\n statIfExists,\n truncateSingleLine,\n} from \"./paths.js\";\nimport {\n TranscriptSourceNames,\n type TranscriptRecord,\n type TranscriptScopeName,\n type TranscriptSourceName,\n} from \"./types.js\";\n\nexport async function discoverTranscriptFiles(\n source: TranscriptScopeName\n): Promise<TranscriptRecord[]> {\n const sources = source === \"all\" ? TranscriptSourceNames : [source];\n const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));\n return records\n .flat()\n .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));\n}\n\nfunction sourceRoots(source: TranscriptSourceName): Promise<TranscriptRecord[]>[] {\n if (source === \"codex\") {\n return [\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/sessions\")),\n discoverJsonlFiles(\"codex\", expandHome(\"~/.codex/archived_sessions\")),\n ];\n }\n\n return [\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/projects\")),\n discoverJsonlFiles(\"claude\", expandHome(\"~/.claude/history.jsonl\")),\n ];\n}\n\nexport async function readTranscriptRecord(record: TranscriptRecord): Promise<TranscriptRecord> {\n const metadata = await readMetadataFromFile(record.source, record.filePath);\n return {\n ...record,\n ...metadata,\n };\n}\n\nexport async function readMetadataFromFile(\n source: TranscriptSourceName,\n filePath: string\n): Promise<Pick<TranscriptRecord, \"cwd\" | \"sessionId\" | \"title\">> {\n const lines = await readJsonlLines(filePath, 80);\n\n for (const line of lines) {\n const parsed = parseJsonObject(line);\n if (!parsed) {\n continue;\n }\n\n if (source === \"codex\") {\n const payload = asObject(parsed.payload);\n if (parsed.type === \"session_meta\" && payload) {\n return {\n cwd: readString(payload.cwd),\n sessionId: readString(payload.session_id) ?? readString(payload.id),\n title: readString(payload.cwd),\n };\n }\n }\n\n if (source === \"claude\") {\n const message = asObject(parsed.message);\n const role = readString(message?.role) ?? readString(parsed.type);\n const content = message ? extractContentText(message.content) : readString(parsed.content);\n if (content && (role === \"user\" || role === \"assistant\")) {\n return {\n cwd: readString(parsed.cwd) ?? readString(parsed.project),\n sessionId: readString(parsed.sessionId),\n title: truncateSingleLine(content, 120),\n };\n }\n }\n }\n\n return {\n sessionId: path.basename(filePath, \".jsonl\"),\n };\n}\n\nexport async function resolveTranscriptFile(\n source: TranscriptScopeName,\n target: string\n): Promise<TranscriptRecord> {\n const expandedTarget = expandHome(target);\n const directStat = await statIfExists(expandedTarget);\n if (directStat?.isFile()) {\n return recordFromPath(detectSourceFromPath(expandedTarget), expandedTarget, directStat.mtime);\n }\n\n const files = await discoverTranscriptFiles(source);\n const normalizedTarget = target.toLowerCase();\n const match = files.find((file) => {\n return (\n file.filePath.toLowerCase().includes(normalizedTarget) ||\n file.sessionId?.toLowerCase().includes(normalizedTarget)\n );\n });\n\n if (!match) {\n throw new Error(`No transcript matched \"${target}\". Run cngkit transcripts list first.`);\n }\n\n return match;\n}\n","export const TranscriptSourceNames = [\"codex\", \"claude\"] as const;\n\nexport type TranscriptSourceName = (typeof TranscriptSourceNames)[number];\nexport type TranscriptScopeName = TranscriptSourceName | \"all\";\n\nexport type TranscriptRecord = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly updatedAt: string;\n readonly sessionId?: string;\n readonly cwd?: string;\n readonly title?: string;\n};\n\nexport type TranscriptEntry = {\n readonly source: TranscriptSourceName;\n readonly filePath: string;\n readonly timestamp?: string;\n readonly role: string;\n readonly text: string;\n};\n\nexport type TranscriptListOptions = {\n readonly source: TranscriptScopeName;\n readonly limit: number;\n};\n\nexport type TranscriptReadOptions = {\n readonly source: TranscriptScopeName;\n readonly target: string;\n readonly limit: number;\n readonly includeInternal: boolean;\n};\n\nexport type TranscriptGrepOptions = {\n readonly source: TranscriptScopeName;\n readonly query: string;\n readonly limit: number;\n readonly fileLimit: number;\n readonly includeInternal: boolean;\n};\n\nexport type JsonObject = Record<string, unknown>;\n","import { Box, Text } from \"ink\";\n\nimport { singleLine } from \"../../shared/command-utils.js\";\nimport type { TranscriptRecord } from \"./reader.js\";\n\nexport function TranscriptRecordList({ records }: { readonly records: readonly TranscriptRecord[] }) {\n if (records.length === 0) {\n return <Text color=\"yellow\">No transcript files found.</Text>;\n }\n\n return (\n <Box flexDirection=\"column\">\n {records.map((record, index) => (\n <Box flexDirection=\"column\" key={`${record.source}-${record.filePath}-${index}`} marginBottom={1}>\n <Text>\n <Text color=\"cyan\">{index + 1}. </Text>\n <Text bold>{record.source}</Text>\n <Text dimColor> {record.updatedAt}</Text>\n </Text>\n <Text>{record.filePath}</Text>\n {record.sessionId ? (\n <Text dimColor>\n session {record.sessionId}\n </Text>\n ) : null}\n {record.cwd ? <Text dimColor>cwd {record.cwd}</Text> : null}\n {record.title ? <Text>{singleLine(record.title)}</Text> : null}\n </Box>\n ))}\n </Box>\n );\n}\n"],"mappings":"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","names":["fs","fs","path","fs","path","path","path","options","fs","args","options","jsx","args","options"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cngkit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.22",
|
|
4
4
|
"description": "Opinionated CNG CLI kit for Coderoom collaboration and operator workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"engines": {
|
|
36
36
|
"node": ">=20"
|
|
37
37
|
},
|
|
38
|
-
"license": "
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@inkkit/ink-markdown": "1.0.0",
|
|
41
41
|
"chalk": "5.6.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/api-client.ts"],"sourcesContent":["import { CngApiClient } from \"@cng/client\";\n\nimport { resolveApiBaseUrl, type GlobalCommandOptions } from \"./config.js\";\nimport { formatError } from \"./output.js\";\n\nexport type BackendHealthStatus =\n | {\n ok: true;\n service: string;\n }\n | {\n ok: false;\n message: string;\n };\n\nexport function createCngApiClient(options: GlobalCommandOptions): CngApiClient {\n return new CngApiClient({\n baseUrl: resolveApiBaseUrl(options),\n timeoutInSeconds: 15,\n maxRetries: 1,\n });\n}\n\nexport async function readBackendHealth(\n options: GlobalCommandOptions\n): Promise<BackendHealthStatus> {\n try {\n const client = createCngApiClient(options);\n const health = await client.system.getHealth();\n return {\n ok: true,\n service: health.service,\n };\n } catch (error) {\n return {\n ok: false,\n message: formatError(error),\n };\n }\n}\n"],"mappings":";;;;;;;AAeO,SAAS,mBAAmB,SAA6C;AAC9E,SAAO,IAAI,aAAa;AAAA,IACtB,SAAS,kBAAkB,OAAO;AAAA,IAClC,kBAAkB;AAAA,IAClB,YAAY;AAAA,EACd,CAAC;AACH;AAEA,eAAsB,kBACpB,SAC8B;AAC9B,MAAI;AACF,UAAM,SAAS,mBAAmB,OAAO;AACzC,UAAM,SAAS,MAAM,OAAO,OAAO,UAAU;AAC7C,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS,OAAO;AAAA,IAClB;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS,YAAY,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/coderoom/run-coderoom-command.ts","../src/features/coderoom/sync/client.ts","../src/features/coderoom/sync/files.ts","../src/features/coderoom/sync/paths.ts","../src/features/coderoom/sync/protocol.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { readBackendHealth } from \"../../shared/api-client.js\";\nimport { createRoomCode, resolveApiBaseUrl, type GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output.js\";\nimport { startSyncSession, type SyncSessionRole } from \"./sync/client.js\";\n\nexport type CoderoomCommandOptions = GlobalCommandOptions;\n\nexport type ShareCommandOptions = GlobalCommandOptions;\n\nexport async function runCoderoomCommand(\n args: string[] | undefined,\n options: CoderoomCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const [subcommand, ...subcommandArgs] = args ?? [];\n\n switch (subcommand) {\n case \"share\":\n return runShareCommand(subcommandArgs[0], options, output);\n case \"join\":\n return runJoinCommand(subcommandArgs[0], options, output);\n default:\n throw new Error(\"Missing coderoom command. Usage: cngkit coderoom <share|join>\");\n }\n}\n\nexport async function runShareCommand(\n roomCode: string | undefined,\n options: ShareCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const syncRoomCode = roomCode ?? createRoomCode();\n\n output.success(`Share code: ${syncRoomCode}`);\n await printBackendStatus(options, output);\n await runSyncSession(\"share\", syncRoomCode, options, output);\n}\n\nexport async function runJoinCommand(\n roomCode: string | undefined,\n options: GlobalCommandOptions,\n output: CommandOutput\n): Promise<void> {\n if (!roomCode) {\n throw new Error(\"Missing room code. Usage: cngkit coderoom join <room-code>\");\n }\n\n await printBackendStatus(options, output);\n await runSyncSession(\"join\", roomCode, options, output);\n}\n\nasync function printBackendStatus(\n options: GlobalCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const health = await readBackendHealth(options);\n if (health.ok) {\n output.success(`API: ${health.service} ready`);\n return;\n }\n\n output.warning(`API: unavailable (${health.message})`);\n}\n\nasync function runSyncSession(\n role: SyncSessionRole,\n roomCode: string,\n options: GlobalCommandOptions,\n output: CommandOutput\n): Promise<void> {\n await startSyncSession({\n apiBaseUrl: resolveApiBaseUrl(options),\n roomCode,\n role,\n cwd: process.cwd(),\n output,\n });\n}\n","import process from \"node:process\";\nimport fs from \"node:fs\";\n\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport WebSocket, { type RawData } from \"ws\";\n\nimport { createPeerId } from \"../../../shared/config.js\";\nimport type { CommandOutput } from \"../../../shared/output.js\";\nimport {\n applyRemoteMessage,\n buildFileMessage,\n collectSnapshotMessages,\n createSuppressionTracker,\n type SuppressionTracker,\n} from \"./files.js\";\nimport {\n resolveRepoContext,\n shouldSyncRelativePath,\n toRepoRelativePath,\n type RepoContext,\n} from \"./paths.js\";\nimport { decodeSyncMessage, encodeSyncMessage, type SyncMessage } from \"./protocol.js\";\n\nexport type SyncSessionRole = \"share\" | \"join\";\n\nexport type StartSyncSessionOptions = {\n apiBaseUrl: string;\n roomCode: string;\n role: SyncSessionRole;\n cwd: string;\n output: CommandOutput;\n};\n\ntype WatcherContext = {\n repoContext: RepoContext;\n socket: WebSocket;\n peerId: string;\n suppressionTracker: SuppressionTracker;\n output: CommandOutput;\n};\n\nfunction createSyncWebSocketUrl(apiBaseUrl: string, roomCode: string): string {\n const url = new URL(apiBaseUrl);\n url.protocol = url.protocol === \"http:\" ? \"ws:\" : \"wss:\";\n url.pathname = `/api/cng/sync/${encodeURIComponent(roomCode)}`;\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n}\n\nfunction sendMessage(socket: WebSocket, message: SyncMessage): void {\n if (socket.readyState === WebSocket.OPEN) {\n socket.send(encodeSyncMessage(message));\n }\n}\n\nexport async function startSyncSession(options: StartSyncSessionOptions): Promise<void> {\n const repoContext = await resolveRepoContext(options.cwd);\n const peerId = createPeerId();\n const suppressionTracker = createSuppressionTracker();\n const webSocketUrl = createSyncWebSocketUrl(options.apiBaseUrl, options.roomCode);\n const socket = new WebSocket(webSocketUrl);\n\n options.output.success(`Room: ${options.roomCode}`);\n options.output.info(`Repo: ${repoContext.rootDir}`);\n options.output.muted(`Peer: ${peerId}`);\n\n const watcherContext: WatcherContext = {\n repoContext,\n socket,\n peerId,\n suppressionTracker,\n output: options.output,\n };\n const watcher = createRepoWatcher(watcherContext);\n\n socket.on(\"open\", () => {\n sendMessage(socket, {\n type: \"hello\",\n peerId,\n role: options.role,\n sentAt: Date.now(),\n });\n void sendInitialSnapshot(socket, repoContext, peerId, options.output);\n });\n\n socket.on(\"message\", (data) => {\n void handleRemoteMessage({\n data,\n repoContext,\n peerId,\n suppressionTracker,\n output: options.output,\n });\n });\n\n await waitForSessionClose(socket, watcher);\n}\n\nfunction createRepoWatcher(context: WatcherContext): FSWatcher {\n const watcher = chokidar.watch(context.repoContext.rootDir, {\n ignoreInitial: true,\n ignored: (candidatePath) => {\n const relativePath = toRepoRelativePath(context.repoContext.rootDir, candidatePath);\n if (relativePath?.split(\"/\").includes(\".git\") ?? false) {\n return true;\n }\n try {\n const stat = fs.statSync(candidatePath);\n return !stat.isFile() && !stat.isDirectory();\n } catch {\n return false;\n }\n },\n });\n\n watcher.on(\"add\", (absolutePath) => {\n void sendLocalFileChange(context, absolutePath);\n });\n watcher.on(\"change\", (absolutePath) => {\n void sendLocalFileChange(context, absolutePath);\n });\n watcher.on(\"unlink\", (absolutePath) => {\n void sendLocalDelete(context, absolutePath);\n });\n watcher.on(\"error\", (watcherError) => {\n context.output.warning(`watcher error: ${watcherError instanceof Error ? watcherError.message : String(watcherError)}`);\n });\n\n return watcher;\n}\n\nasync function sendInitialSnapshot(\n socket: WebSocket,\n repoContext: RepoContext,\n peerId: string,\n output: CommandOutput\n): Promise<void> {\n let fileCount = 0;\n\n for await (const message of collectSnapshotMessages(repoContext, peerId)) {\n sendMessage(socket, message);\n fileCount += 1;\n }\n\n sendMessage(socket, {\n type: \"snapshot-complete\",\n peerId,\n sentAt: Date.now(),\n fileCount,\n });\n output.success(`sent snapshot ${fileCount} files`);\n}\n\nasync function sendLocalFileChange(context: WatcherContext, absolutePath: string): Promise<void> {\n const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);\n if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {\n return;\n }\n\n const message = await buildFileMessage(context.repoContext, context.peerId, relativePath);\n if (!message) {\n return;\n }\n\n sendMessage(context.socket, message);\n context.output.muted(`sent file ${relativePath}`);\n}\n\nasync function sendLocalDelete(context: WatcherContext, absolutePath: string): Promise<void> {\n const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);\n if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {\n return;\n }\n\n if (!(await shouldSyncRelativePath(context.repoContext, relativePath))) {\n return;\n }\n\n sendMessage(context.socket, {\n type: \"delete\",\n peerId: context.peerId,\n path: relativePath,\n mtimeMs: Date.now(),\n sentAt: Date.now(),\n });\n context.output.warning(`sent delete ${relativePath}`);\n}\n\nasync function handleRemoteMessage(options: {\n data: RawData;\n repoContext: RepoContext;\n peerId: string;\n suppressionTracker: SuppressionTracker;\n output: CommandOutput;\n}): Promise<void> {\n const decodedMessage = decodeSyncMessage(options.data.toString());\n if (!decodedMessage || decodedMessage.peerId === options.peerId) {\n return;\n }\n\n if (decodedMessage.type === \"hello\") {\n options.output.info(`peer joined ${decodedMessage.peerId}`);\n return;\n }\n\n if (decodedMessage.type === \"snapshot-complete\") {\n options.output.success(`peer snapshot complete ${decodedMessage.fileCount} files`);\n return;\n }\n\n await applyRemoteMessage(options.repoContext, decodedMessage, options.suppressionTracker);\n options.output.success(`applied ${decodedMessage.type} ${decodedMessage.path}`);\n}\n\nasync function waitForSessionClose(socket: WebSocket, watcher: FSWatcher): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n\n const closeSession = async (): Promise<void> => {\n await watcher.close();\n if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {\n socket.close();\n }\n };\n\n const finish = async (): Promise<void> => {\n if (settled) {\n return;\n }\n settled = true;\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n await closeSession();\n resolve();\n };\n\n const onSignal = (): void => {\n void finish();\n };\n\n socket.on(\"error\", async (error) => {\n if (settled) {\n return;\n }\n settled = true;\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n await closeSession();\n reject(error);\n });\n\n socket.on(\"close\", () => {\n void finish();\n });\n\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n });\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { SyncFileMutationMessage, SyncMessage } from \"./protocol.js\";\nimport {\n resolveRepoPath,\n shouldSyncRelativePath,\n toRepoRelativePath,\n type RepoContext,\n} from \"./paths.js\";\n\nexport type SuppressionTracker = {\n suppress(relativePath: string): void;\n isSuppressed(relativePath: string): boolean;\n};\n\nexport function createSuppressionTracker(windowMs = 1500): SuppressionTracker {\n const suppressedUntilByPath = new Map<string, number>();\n\n return {\n suppress(relativePath: string) {\n suppressedUntilByPath.set(relativePath, Date.now() + windowMs);\n },\n isSuppressed(relativePath: string) {\n const suppressedUntil = suppressedUntilByPath.get(relativePath);\n if (!suppressedUntil) {\n return false;\n }\n if (suppressedUntil < Date.now()) {\n suppressedUntilByPath.delete(relativePath);\n return false;\n }\n return true;\n },\n };\n}\n\nexport async function* collectSnapshotMessages(\n context: RepoContext,\n peerId: string\n): AsyncGenerator<SyncMessage> {\n yield* collectDirectorySnapshot(context, context.rootDir, peerId);\n}\n\nasync function* collectDirectorySnapshot(\n context: RepoContext,\n directoryPath: string,\n peerId: string\n): AsyncGenerator<SyncMessage> {\n const entries = await fs.opendir(directoryPath);\n\n for await (const entry of entries) {\n const absolutePath = path.join(directoryPath, entry.name);\n const relativePath = toRepoRelativePath(context.rootDir, absolutePath);\n\n if (!relativePath || !(await shouldSyncRelativePath(context, relativePath))) {\n continue;\n }\n\n if (entry.isDirectory()) {\n yield* collectDirectorySnapshot(context, absolutePath, peerId);\n continue;\n }\n\n if (!entry.isFile()) {\n continue;\n }\n\n const [content, stat] = await Promise.all([fs.readFile(absolutePath), fs.stat(absolutePath)]);\n yield {\n type: \"file\",\n peerId,\n path: relativePath,\n contentBase64: content.toString(\"base64\"),\n mtimeMs: stat.mtimeMs,\n sentAt: Date.now(),\n };\n }\n}\n\nexport async function buildFileMessage(\n context: RepoContext,\n peerId: string,\n relativePath: string\n): Promise<SyncMessage | undefined> {\n if (!(await shouldSyncRelativePath(context, relativePath))) {\n return undefined;\n }\n\n const absolutePath = resolveRepoPath(context.rootDir, relativePath);\n if (!absolutePath) {\n return undefined;\n }\n\n const stat = await fs.stat(absolutePath);\n if (!stat.isFile()) {\n return undefined;\n }\n\n const content = await fs.readFile(absolutePath);\n return {\n type: \"file\",\n peerId,\n path: relativePath,\n contentBase64: content.toString(\"base64\"),\n mtimeMs: stat.mtimeMs,\n sentAt: Date.now(),\n };\n}\n\nexport async function applyRemoteMessage(\n context: RepoContext,\n message: SyncFileMutationMessage,\n suppressionTracker: SuppressionTracker\n): Promise<void> {\n const absolutePath = resolveRepoPath(context.rootDir, message.path);\n if (!absolutePath || !(await shouldSyncRelativePath(context, message.path))) {\n return;\n }\n\n suppressionTracker.suppress(message.path);\n\n if (message.type === \"delete\") {\n await fs.rm(absolutePath, { force: true });\n return;\n }\n\n await fs.mkdir(path.dirname(absolutePath), { recursive: true });\n await fs.writeFile(absolutePath, Buffer.from(message.contentBase64, \"base64\"));\n}\n","import { execFile } from \"node:child_process\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type RepoContext = {\n rootDir: string;\n};\n\nexport async function resolveRepoContext(cwd: string): Promise<RepoContext> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n });\n return { rootDir: path.resolve(stdout.trim()) };\n } catch {\n return { rootDir: path.resolve(cwd) };\n }\n}\n\nexport function toRepoRelativePath(rootDir: string, absolutePath: string): string | undefined {\n const relativePath = path.relative(rootDir, absolutePath);\n\n if (!relativePath || relativePath.startsWith(\"..\") || path.isAbsolute(relativePath)) {\n return undefined;\n }\n\n return relativePath.split(path.sep).join(\"/\");\n}\n\nexport function resolveRepoPath(rootDir: string, relativePath: string): string | undefined {\n if (!isSafeRelativePath(relativePath)) {\n return undefined;\n }\n\n const absolutePath = path.resolve(rootDir, relativePath);\n const normalizedRoot = `${path.resolve(rootDir)}${path.sep}`;\n\n if (absolutePath !== path.resolve(rootDir) && !absolutePath.startsWith(normalizedRoot)) {\n return undefined;\n }\n\n return absolutePath;\n}\n\nexport function isSafeRelativePath(relativePath: string): boolean {\n if (!relativePath || relativePath.startsWith(\"/\") || relativePath.includes(\"\\0\")) {\n return false;\n }\n\n const normalizedParts = relativePath.split(/[\\\\/]+/).filter(Boolean);\n if (normalizedParts.includes(\"..\")) {\n return false;\n }\n\n return normalizedParts[0] !== \".git\";\n}\n\nexport async function shouldSyncRelativePath(\n context: RepoContext,\n relativePath: string\n): Promise<boolean> {\n if (!isSafeRelativePath(relativePath)) {\n return false;\n }\n\n try {\n await execFileAsync(\"git\", [\"check-ignore\", \"--quiet\", \"--\", relativePath], {\n cwd: context.rootDir,\n });\n return false;\n } catch (error) {\n const exitCode =\n typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n return exitCode === 1;\n }\n}\n","import { z } from \"zod\";\n\nconst SyncBaseMessageSchema = z.object({\n peerId: z.string().min(1),\n sentAt: z.number().finite(),\n});\n\nexport const SyncHelloMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"hello\"),\n role: z.union([z.literal(\"share\"), z.literal(\"join\")]),\n});\n\nexport const SyncFileMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"file\"),\n path: z.string().min(1),\n contentBase64: z.string(),\n mtimeMs: z.number().finite(),\n});\n\nexport const SyncDeleteMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"delete\"),\n path: z.string().min(1),\n mtimeMs: z.number().finite(),\n});\n\nexport const SyncSnapshotCompleteMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"snapshot-complete\"),\n fileCount: z.number().int().nonnegative(),\n});\n\nexport const SyncMessageSchema = z.discriminatedUnion(\"type\", [\n SyncHelloMessageSchema,\n SyncFileMessageSchema,\n SyncDeleteMessageSchema,\n SyncSnapshotCompleteMessageSchema,\n]);\n\nexport type SyncMessage = z.infer<typeof SyncMessageSchema>;\nexport type SyncFileMutationMessage = Extract<SyncMessage, { type: \"file\" | \"delete\" }>;\n\nexport function encodeSyncMessage(message: SyncMessage): string {\n return JSON.stringify(SyncMessageSchema.parse(message));\n}\n\nexport function decodeSyncMessage(value: unknown): SyncMessage | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n try {\n return SyncMessageSchema.parse(JSON.parse(value));\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":["process","fs","path","path","fs","process"]}
|
package/dist/chunk-MRXGD6TC.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CommandOutputMessages,
|
|
3
|
-
createCommandOutput,
|
|
4
|
-
formatError
|
|
5
|
-
} from "./chunk-WDI43VPW.js";
|
|
6
|
-
|
|
7
|
-
// src/cli/command-runner.tsx
|
|
8
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
-
import { useApp } from "ink";
|
|
10
|
-
import { jsx } from "react/jsx-runtime";
|
|
11
|
-
function CommandRunner({ run }) {
|
|
12
|
-
const { exit } = useApp();
|
|
13
|
-
const nextMessageIdRef = useRef(1);
|
|
14
|
-
const [messages, setMessages] = useState([]);
|
|
15
|
-
const appendMessage = useCallback((message) => {
|
|
16
|
-
const nextMessage = {
|
|
17
|
-
...message,
|
|
18
|
-
id: nextMessageIdRef.current
|
|
19
|
-
};
|
|
20
|
-
nextMessageIdRef.current += 1;
|
|
21
|
-
setMessages((currentMessages) => [...currentMessages, nextMessage]);
|
|
22
|
-
}, []);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const output = createCommandOutput({
|
|
25
|
-
appendMessage,
|
|
26
|
-
stdout: process.stdout,
|
|
27
|
-
stderr: process.stderr
|
|
28
|
-
});
|
|
29
|
-
void run(output).catch((error) => {
|
|
30
|
-
process.exitCode = 1;
|
|
31
|
-
output.error(formatError(error));
|
|
32
|
-
}).finally(() => {
|
|
33
|
-
setTimeout(() => exit(), 0);
|
|
34
|
-
});
|
|
35
|
-
}, [appendMessage, exit, run]);
|
|
36
|
-
return /* @__PURE__ */ jsx(CommandOutputMessages, { messages });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
CommandRunner
|
|
41
|
-
};
|
|
42
|
-
//# sourceMappingURL=chunk-MRXGD6TC.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/command-runner.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useApp } from \"ink\";\n\nimport {\n CommandOutputMessages,\n createCommandOutput,\n formatError,\n type CommandOutput,\n type CommandOutputMessage,\n type CommandOutputMessagePayload,\n} from \"../shared/output.js\";\n\ntype CommandRunnerProps = {\n readonly run: (output: CommandOutput) => Promise<void>;\n};\n\nexport function CommandRunner({ run }: CommandRunnerProps) {\n const { exit } = useApp();\n const nextMessageIdRef = useRef(1);\n const [messages, setMessages] = useState<CommandOutputMessage[]>([]);\n const appendMessage = useCallback((message: CommandOutputMessagePayload) => {\n const nextMessage: CommandOutputMessage = {\n ...message,\n id: nextMessageIdRef.current,\n };\n nextMessageIdRef.current += 1;\n setMessages((currentMessages) => [...currentMessages, nextMessage]);\n }, []);\n\n useEffect(() => {\n const output = createCommandOutput({\n appendMessage,\n stdout: process.stdout,\n stderr: process.stderr,\n });\n\n void run(output)\n .catch((error: unknown) => {\n process.exitCode = 1;\n output.error(formatError(error));\n })\n .finally(() => {\n setTimeout(() => exit(), 0);\n });\n }, [appendMessage, exit, run]);\n\n return <CommandOutputMessages messages={messages} />;\n}\n"],"mappings":";;;;;;;AAAA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAS,cAAc;AA6Cd;AA9BF,SAAS,cAAc,EAAE,IAAI,GAAuB;AACzD,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,mBAAmB,OAAO,CAAC;AACjC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAiC,CAAC,CAAC;AACnE,QAAM,gBAAgB,YAAY,CAAC,YAAyC;AAC1E,UAAM,cAAoC;AAAA,MACxC,GAAG;AAAA,MACH,IAAI,iBAAiB;AAAA,IACvB;AACA,qBAAiB,WAAW;AAC5B,gBAAY,CAAC,oBAAoB,CAAC,GAAG,iBAAiB,WAAW,CAAC;AAAA,EACpE,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,UAAM,SAAS,oBAAoB;AAAA,MACjC;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,SAAK,IAAI,MAAM,EACZ,MAAM,CAAC,UAAmB;AACzB,cAAQ,WAAW;AACnB,aAAO,MAAM,YAAY,KAAK,CAAC;AAAA,IACjC,CAAC,EACA,QAAQ,MAAM;AACb,iBAAW,MAAM,KAAK,GAAG,CAAC;AAAA,IAC5B,CAAC;AAAA,EACL,GAAG,CAAC,eAAe,MAAM,GAAG,CAAC;AAE7B,SAAO,oBAAC,yBAAsB,UAAoB;AACpD;","names":[]}
|