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.
Files changed (2) hide show
  1. package/dist/index.js +63 -27
  2. 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 logResult = await gitClient.log({
615
- from: hash,
616
- to: hash,
617
- maxCount: 1,
618
- format: {
619
- fullHash: "%H",
620
- message: "%s",
621
- author: "%an",
622
- timestamp: "%at",
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 filesRaw = await gitClient.show(["--name-status", "--format=", hash]);
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 diff = await gitClient.show(["--patch", "--format=", hash]);
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: latest.fullHash.substring(0, 7),
656
- fullHash: latest.fullHash,
657
- message: latest.message,
658
- author: latest.author,
659
- date: parseInt(latest.timestamp, 10) * 1000,
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.v === 1) {
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
- controlWs.send(JSON.stringify(response));
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.v === 1) {
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
- dataWs.send(JSON.stringify(response));
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.38",
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": {