lunel-cli 0.1.36 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +106 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -220,6 +220,21 @@ function assertSafePath(requestedPath) {
|
|
|
220
220
|
// ============================================================================
|
|
221
221
|
// File System Handlers
|
|
222
222
|
// ============================================================================
|
|
223
|
+
function isLikelyBinaryBuffer(buffer) {
|
|
224
|
+
if (buffer.length === 0)
|
|
225
|
+
return false;
|
|
226
|
+
if (buffer.includes(0x00))
|
|
227
|
+
return true;
|
|
228
|
+
let suspicious = 0;
|
|
229
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
230
|
+
const byte = buffer[i];
|
|
231
|
+
const isPrintableAscii = byte >= 0x20 && byte <= 0x7e;
|
|
232
|
+
const isCommonControl = byte === 0x09 || byte === 0x0a || byte === 0x0d;
|
|
233
|
+
if (!isPrintableAscii && !isCommonControl)
|
|
234
|
+
suspicious++;
|
|
235
|
+
}
|
|
236
|
+
return suspicious / buffer.length > 0.3;
|
|
237
|
+
}
|
|
223
238
|
async function handleFsLs(payload) {
|
|
224
239
|
const reqPath = payload.path || ".";
|
|
225
240
|
const safePath = assertSafePath(reqPath);
|
|
@@ -258,6 +273,25 @@ async function handleFsStat(payload) {
|
|
|
258
273
|
mtime: stat.mtimeMs,
|
|
259
274
|
mode: stat.mode,
|
|
260
275
|
};
|
|
276
|
+
if (stat.isFile()) {
|
|
277
|
+
try {
|
|
278
|
+
const fd = await fs.open(safePath, "r");
|
|
279
|
+
try {
|
|
280
|
+
const sampleSize = Math.min(stat.size, 8192);
|
|
281
|
+
const sample = Buffer.alloc(sampleSize);
|
|
282
|
+
if (sampleSize > 0) {
|
|
283
|
+
await fd.read(sample, 0, sampleSize, 0);
|
|
284
|
+
}
|
|
285
|
+
result.isBinary = isLikelyBinaryBuffer(sample);
|
|
286
|
+
}
|
|
287
|
+
finally {
|
|
288
|
+
await fd.close();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
// Keep stat resilient even if sampling fails
|
|
293
|
+
}
|
|
294
|
+
}
|
|
261
295
|
return result;
|
|
262
296
|
}
|
|
263
297
|
async function handleFsRead(payload) {
|
|
@@ -268,8 +302,8 @@ async function handleFsRead(payload) {
|
|
|
268
302
|
// Check if binary
|
|
269
303
|
const stat = await fs.stat(safePath);
|
|
270
304
|
const content = await fs.readFile(safePath);
|
|
271
|
-
//
|
|
272
|
-
const isBinary = content.
|
|
305
|
+
// Detect if binary
|
|
306
|
+
const isBinary = isLikelyBinaryBuffer(content.subarray(0, 8192));
|
|
273
307
|
if (isBinary) {
|
|
274
308
|
return {
|
|
275
309
|
path: reqPath,
|
|
@@ -570,6 +604,73 @@ async function handleGitLog(payload) {
|
|
|
570
604
|
});
|
|
571
605
|
return { commits };
|
|
572
606
|
}
|
|
607
|
+
async function handleGitCommitDetails(payload) {
|
|
608
|
+
const hash = payload.hash?.trim();
|
|
609
|
+
if (!hash)
|
|
610
|
+
throw Object.assign(new Error("hash is required"), { code: "EINVAL" });
|
|
611
|
+
try {
|
|
612
|
+
const commitResult = await runGit(["show", "-s", "--format=%H%n%s%n%an%n%at", hash]);
|
|
613
|
+
if (commitResult.code !== 0 || !commitResult.stdout.trim()) {
|
|
614
|
+
throw Object.assign(new Error("Commit not found"), { code: "EGIT" });
|
|
615
|
+
}
|
|
616
|
+
const commitLines = commitResult.stdout.split(/\r?\n/);
|
|
617
|
+
const fullHash = commitLines[0]?.trim() || "";
|
|
618
|
+
const message = commitLines[1] ?? "";
|
|
619
|
+
const author = commitLines[2] ?? "";
|
|
620
|
+
const timestamp = Number.parseInt(commitLines[3] ?? "0", 10);
|
|
621
|
+
if (!fullHash) {
|
|
622
|
+
throw Object.assign(new Error("Commit not found"), { code: "EGIT" });
|
|
623
|
+
}
|
|
624
|
+
const filesResult = await runGit(["show", "--name-status", "--format=", hash]);
|
|
625
|
+
if (filesResult.code !== 0) {
|
|
626
|
+
throw Object.assign(new Error(filesResult.stderr || "git show failed"), { code: "EGIT" });
|
|
627
|
+
}
|
|
628
|
+
const filesRaw = filesResult.stdout;
|
|
629
|
+
const files = filesRaw
|
|
630
|
+
.split(/\r?\n/)
|
|
631
|
+
.map((line) => line.trim())
|
|
632
|
+
.filter(Boolean)
|
|
633
|
+
.map((line) => {
|
|
634
|
+
const parts = line.split("\t");
|
|
635
|
+
const status = parts[0] || "?";
|
|
636
|
+
// Handles regular + rename/copy name-status output.
|
|
637
|
+
const path = parts[2] || parts[1] || "";
|
|
638
|
+
return { status, path };
|
|
639
|
+
})
|
|
640
|
+
.filter((entry) => !!entry.path);
|
|
641
|
+
const diffResult = await runGit(["show", "--patch", "--format=", hash]);
|
|
642
|
+
if (diffResult.code !== 0) {
|
|
643
|
+
throw Object.assign(new Error(diffResult.stderr || "git show failed"), { code: "EGIT" });
|
|
644
|
+
}
|
|
645
|
+
const diff = diffResult.stdout;
|
|
646
|
+
const fileDiffs = {};
|
|
647
|
+
const fileChunks = diff.split(/^diff --git /m).filter(Boolean);
|
|
648
|
+
for (const chunk of fileChunks) {
|
|
649
|
+
const patch = `diff --git ${chunk}`;
|
|
650
|
+
const firstLine = chunk.split(/\r?\n/, 1)[0] || "";
|
|
651
|
+
const match = firstLine.match(/^a\/(.+?) b\/(.+)$/);
|
|
652
|
+
if (match?.[2]) {
|
|
653
|
+
fileDiffs[match[2]] = patch;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
commit: {
|
|
658
|
+
hash: fullHash.substring(0, 7),
|
|
659
|
+
fullHash,
|
|
660
|
+
message,
|
|
661
|
+
author,
|
|
662
|
+
date: timestamp * 1000,
|
|
663
|
+
},
|
|
664
|
+
files,
|
|
665
|
+
diff,
|
|
666
|
+
fileDiffs,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
catch (err) {
|
|
670
|
+
const message = err instanceof Error ? err.message : "git show failed";
|
|
671
|
+
throw Object.assign(new Error(message), { code: "EGIT" });
|
|
672
|
+
}
|
|
673
|
+
}
|
|
573
674
|
async function handleGitDiff(payload) {
|
|
574
675
|
const filepath = payload.path;
|
|
575
676
|
const staged = payload.staged === true;
|
|
@@ -2110,6 +2211,9 @@ async function processMessage(message) {
|
|
|
2110
2211
|
case "log":
|
|
2111
2212
|
result = await handleGitLog(payload);
|
|
2112
2213
|
break;
|
|
2214
|
+
case "commitDetails":
|
|
2215
|
+
result = await handleGitCommitDetails(payload);
|
|
2216
|
+
break;
|
|
2113
2217
|
case "diff":
|
|
2114
2218
|
result = await handleGitDiff(payload);
|
|
2115
2219
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lunel-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"author": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Soham Bharambe",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"license": "Functional Source License, Version 1.1, Apache 2.0 Future License",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"bin": {
|
|
17
|
-
"lunel-cli": "
|
|
17
|
+
"lunel-cli": "dist/index.js"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|