lunel-cli 0.1.38 → 0.1.40
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 +63 -27
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -11,7 +11,6 @@ import * as os from "os";
|
|
|
11
11
|
import { spawn, execSync, execFileSync } from "child_process";
|
|
12
12
|
import { createServer, createConnection } from "net";
|
|
13
13
|
import { createInterface } from "readline";
|
|
14
|
-
import { simpleGit } from "simple-git";
|
|
15
14
|
const DEFAULT_PROXY_URL = process.env.LUNEL_PROXY_URL || "https://gateway.lunel.dev";
|
|
16
15
|
const MANAGER_URL = process.env.LUNEL_MANAGER_URL || "https://manager.lunel.dev";
|
|
17
16
|
const CLI_ARGS = process.argv.slice(2);
|
|
@@ -23,7 +22,6 @@ const PTY_RELEASE_INFO_URL = process.env.LUNEL_PTY_INFO_URL ||
|
|
|
23
22
|
const VERBOSE_AI_LOGS = process.env.LUNEL_DEBUG_AI === "1";
|
|
24
23
|
// Root directory - sandbox all file operations to this
|
|
25
24
|
const ROOT_DIR = process.cwd();
|
|
26
|
-
const gitClient = simpleGit(ROOT_DIR);
|
|
27
25
|
// Terminal sessions (managed by Rust PTY binary)
|
|
28
26
|
const terminals = new Set();
|
|
29
27
|
// PTY binary process
|
|
@@ -200,6 +198,24 @@ function samePortSet(a, b) {
|
|
|
200
198
|
}
|
|
201
199
|
return true;
|
|
202
200
|
}
|
|
201
|
+
function isProtocolRequest(value) {
|
|
202
|
+
if (!value || typeof value !== "object")
|
|
203
|
+
return false;
|
|
204
|
+
const msg = value;
|
|
205
|
+
return (msg.v === 1 &&
|
|
206
|
+
typeof msg.id === "string" &&
|
|
207
|
+
typeof msg.ns === "string" &&
|
|
208
|
+
typeof msg.action === "string" &&
|
|
209
|
+
typeof msg.payload === "object" &&
|
|
210
|
+
msg.payload !== null &&
|
|
211
|
+
typeof msg.ok === "undefined");
|
|
212
|
+
}
|
|
213
|
+
function isProtocolResponse(value) {
|
|
214
|
+
if (!value || typeof value !== "object")
|
|
215
|
+
return false;
|
|
216
|
+
const msg = value;
|
|
217
|
+
return msg.v === 1 && typeof msg.id === "string" && typeof msg.ok === "boolean";
|
|
218
|
+
}
|
|
203
219
|
// ============================================================================
|
|
204
220
|
// Path Safety
|
|
205
221
|
// ============================================================================
|
|
@@ -607,26 +623,27 @@ async function handleGitLog(payload) {
|
|
|
607
623
|
return { commits };
|
|
608
624
|
}
|
|
609
625
|
async function handleGitCommitDetails(payload) {
|
|
610
|
-
const hash = payload.hash;
|
|
626
|
+
const hash = payload.hash?.trim();
|
|
611
627
|
if (!hash)
|
|
612
628
|
throw Object.assign(new Error("hash is required"), { code: "EINVAL" });
|
|
613
629
|
try {
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
});
|
|
625
|
-
const latest = logResult.latest;
|
|
626
|
-
if (!latest) {
|
|
630
|
+
const commitResult = await runGit(["show", "-s", "--format=%H%n%s%n%an%n%at", hash]);
|
|
631
|
+
if (commitResult.code !== 0 || !commitResult.stdout.trim()) {
|
|
632
|
+
throw Object.assign(new Error("Commit not found"), { code: "EGIT" });
|
|
633
|
+
}
|
|
634
|
+
const commitLines = commitResult.stdout.split(/\r?\n/);
|
|
635
|
+
const fullHash = commitLines[0]?.trim() || "";
|
|
636
|
+
const message = commitLines[1] ?? "";
|
|
637
|
+
const author = commitLines[2] ?? "";
|
|
638
|
+
const timestamp = Number.parseInt(commitLines[3] ?? "0", 10);
|
|
639
|
+
if (!fullHash) {
|
|
627
640
|
throw Object.assign(new Error("Commit not found"), { code: "EGIT" });
|
|
628
641
|
}
|
|
629
|
-
const
|
|
642
|
+
const filesResult = await runGit(["show", "--name-status", "--format=", hash]);
|
|
643
|
+
if (filesResult.code !== 0) {
|
|
644
|
+
throw Object.assign(new Error(filesResult.stderr || "git show failed"), { code: "EGIT" });
|
|
645
|
+
}
|
|
646
|
+
const filesRaw = filesResult.stdout;
|
|
630
647
|
const files = filesRaw
|
|
631
648
|
.split(/\r?\n/)
|
|
632
649
|
.map((line) => line.trim())
|
|
@@ -639,7 +656,11 @@ async function handleGitCommitDetails(payload) {
|
|
|
639
656
|
return { status, path };
|
|
640
657
|
})
|
|
641
658
|
.filter((entry) => !!entry.path);
|
|
642
|
-
const
|
|
659
|
+
const diffResult = await runGit(["show", "--patch", "--format=", hash]);
|
|
660
|
+
if (diffResult.code !== 0) {
|
|
661
|
+
throw Object.assign(new Error(diffResult.stderr || "git show failed"), { code: "EGIT" });
|
|
662
|
+
}
|
|
663
|
+
const diff = diffResult.stdout;
|
|
643
664
|
const fileDiffs = {};
|
|
644
665
|
const fileChunks = diff.split(/^diff --git /m).filter(Boolean);
|
|
645
666
|
for (const chunk of fileChunks) {
|
|
@@ -652,11 +673,11 @@ async function handleGitCommitDetails(payload) {
|
|
|
652
673
|
}
|
|
653
674
|
return {
|
|
654
675
|
commit: {
|
|
655
|
-
hash:
|
|
656
|
-
fullHash
|
|
657
|
-
message
|
|
658
|
-
author
|
|
659
|
-
date:
|
|
676
|
+
hash: fullHash.substring(0, 7),
|
|
677
|
+
fullHash,
|
|
678
|
+
message,
|
|
679
|
+
author,
|
|
680
|
+
date: timestamp * 1000,
|
|
660
681
|
},
|
|
661
682
|
files,
|
|
662
683
|
diff,
|
|
@@ -2402,6 +2423,9 @@ async function processMessage(message) {
|
|
|
2402
2423
|
};
|
|
2403
2424
|
}
|
|
2404
2425
|
}
|
|
2426
|
+
function sendResponseOnData(response, dataWs) {
|
|
2427
|
+
dataWs.send(JSON.stringify(response));
|
|
2428
|
+
}
|
|
2405
2429
|
function normalizeGatewayUrl(input) {
|
|
2406
2430
|
if (/^https?:\/\//.test(input))
|
|
2407
2431
|
return input.replace(/\/+$/, "");
|
|
@@ -2558,10 +2582,16 @@ async function connectWebSocket() {
|
|
|
2558
2582
|
return;
|
|
2559
2583
|
}
|
|
2560
2584
|
}
|
|
2561
|
-
if (message
|
|
2585
|
+
if (isProtocolResponse(message)) {
|
|
2586
|
+
// Ignore server/app responses forwarded over WS; CLI only processes requests.
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
if (isProtocolRequest(message)) {
|
|
2562
2590
|
const response = await processMessage(message);
|
|
2563
|
-
|
|
2591
|
+
sendResponseOnData(response, dataWs);
|
|
2592
|
+
return;
|
|
2564
2593
|
}
|
|
2594
|
+
console.warn("[router] Ignoring non-request control frame");
|
|
2565
2595
|
}
|
|
2566
2596
|
catch (error) {
|
|
2567
2597
|
console.error("Error processing control message:", error);
|
|
@@ -2590,10 +2620,16 @@ async function connectWebSocket() {
|
|
|
2590
2620
|
const message = JSON.parse(data.toString());
|
|
2591
2621
|
if (message.type === "connected")
|
|
2592
2622
|
return;
|
|
2593
|
-
if (message
|
|
2623
|
+
if (isProtocolResponse(message)) {
|
|
2624
|
+
// Ignore server/app responses forwarded over WS; CLI only processes requests.
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
if (isProtocolRequest(message)) {
|
|
2594
2628
|
const response = await processMessage(message);
|
|
2595
|
-
|
|
2629
|
+
sendResponseOnData(response, dataWs);
|
|
2630
|
+
return;
|
|
2596
2631
|
}
|
|
2632
|
+
console.warn("[router] Ignoring non-request data frame");
|
|
2597
2633
|
}
|
|
2598
2634
|
catch (error) {
|
|
2599
2635
|
console.error("Error processing data message:", error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lunel-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"author": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Soham Bharambe",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"@opencode-ai/sdk": "^1.1.56",
|
|
30
30
|
"ignore": "^6.0.2",
|
|
31
31
|
"qrcode-terminal": "^0.12.0",
|
|
32
|
-
"simple-git": "^3.32.3",
|
|
33
32
|
"ws": "^8.18.0"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|