lunel-cli 0.1.82 → 0.1.83

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/ai/codex.js CHANGED
@@ -6,6 +6,7 @@ import * as path from "path";
6
6
  import { spawn } from "child_process";
7
7
  import { createInterface } from "readline";
8
8
  const THREAD_LIST_SOURCE_KINDS = ["cli", "vscode", "appServer", "exec", "unknown"];
9
+ const DEBUG_MODE = process.env.LUNEL_DEBUG === "1" || process.env.LUNEL_DEBUG_AI === "1";
9
10
  export class CodexProvider {
10
11
  proc = null;
11
12
  shuttingDown = false;
@@ -19,7 +20,8 @@ export class CodexProvider {
19
20
  assistantMessageIdByTurnId = new Map();
20
21
  partTextById = new Map();
21
22
  async init() {
22
- console.log("Starting Codex app-server...");
23
+ if (DEBUG_MODE)
24
+ console.log("Starting Codex app-server...");
23
25
  this.proc = spawn("codex", ["app-server"], {
24
26
  stdio: ["pipe", "pipe", "inherit"],
25
27
  env: process.env,
@@ -38,7 +40,8 @@ export class CodexProvider {
38
40
  }
39
41
  });
40
42
  await this.call("initialize", { clientInfo: { name: "lunel", version: "1.0" } });
41
- console.log("Codex ready.\n");
43
+ if (DEBUG_MODE)
44
+ console.log("Codex ready.\n");
42
45
  }
43
46
  async destroy() {
44
47
  this.shuttingDown = true;
package/dist/ai/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // AI manager — runs both OpenCode and Codex simultaneously and routes calls
2
2
  // by the `backend` field in each request. Backends that fail to init are
3
3
  // skipped gracefully; the available list is exposed to the app.
4
+ const DEBUG_MODE = process.env.LUNEL_DEBUG === "1" || process.env.LUNEL_DEBUG_AI === "1";
4
5
  export class AiManager {
5
6
  _providers = {};
6
7
  _available = [];
@@ -12,7 +13,9 @@ export class AiManager {
12
13
  if (this._available.length === 0) {
13
14
  throw new Error("No AI backend could be started. Ensure opencode or codex is installed.");
14
15
  }
15
- console.log(`[ai] Available backends: ${this._available.join(", ")}`);
16
+ if (DEBUG_MODE) {
17
+ console.log(`[ai] Available backends: ${this._available.join(", ")}`);
18
+ }
16
19
  }
17
20
  async tryInit(backend) {
18
21
  try {
@@ -31,7 +34,9 @@ export class AiManager {
31
34
  this._available.push(backend);
32
35
  }
33
36
  catch (err) {
34
- console.warn(`[ai] ${backend} backend unavailable: ${err.message}`);
37
+ if (DEBUG_MODE) {
38
+ console.warn(`[ai] ${backend} backend unavailable: ${err.message}`);
39
+ }
35
40
  }
36
41
  }
37
42
  availableBackends() {
@@ -2,7 +2,7 @@
2
2
  // All logic extracted verbatim from cli/src/index.ts AI handlers section.
3
3
  import * as crypto from "crypto";
4
4
  import { createOpencodeServer, createOpencodeClient } from "@opencode-ai/sdk";
5
- const VERBOSE_AI_LOGS = process.env.LUNEL_DEBUG_AI === "1";
5
+ const VERBOSE_AI_LOGS = process.env.LUNEL_DEBUG === "1" || process.env.LUNEL_DEBUG_AI === "1";
6
6
  const SSE_BACKOFF_INITIAL_MS = 500;
7
7
  const SSE_BACKOFF_CAP_MS = 30_000;
8
8
  const SSE_MAX_RETRIES = 20;
@@ -39,18 +39,21 @@ export class OpenCodeProvider {
39
39
  process.env.OPENCODE_SERVER_USERNAME = opencodeUsername;
40
40
  process.env.OPENCODE_SERVER_PASSWORD = opencodePassword;
41
41
  this.authHeader = authHeader;
42
- console.log("Starting OpenCode...");
42
+ if (VERBOSE_AI_LOGS)
43
+ console.log("Starting OpenCode...");
43
44
  this.server = await createOpencodeServer({
44
45
  hostname: "127.0.0.1",
45
46
  port: 0,
46
47
  timeout: 15000,
47
48
  });
48
- console.log(`OpenCode server listening on ${this.server.url}`);
49
+ if (VERBOSE_AI_LOGS)
50
+ console.log(`OpenCode server listening on ${this.server.url}`);
49
51
  this.client = createOpencodeClient({
50
52
  baseUrl: this.server.url,
51
53
  headers: { Authorization: authHeader },
52
54
  });
53
- console.log("OpenCode ready.\n");
55
+ if (VERBOSE_AI_LOGS)
56
+ console.log("OpenCode ready.\n");
54
57
  }
55
58
  async destroy() {
56
59
  this.shuttingDown = true;
package/dist/index.js CHANGED
@@ -15,6 +15,15 @@ import { createInterface } from "readline";
15
15
  const DEFAULT_PROXY_URL = normalizeGatewayUrl(process.env.LUNEL_PROXY_URL || "https://gateway.lunel.dev");
16
16
  const MANAGER_URL = normalizeGatewayUrl(process.env.LUNEL_MANAGER_URL || "https://manager.lunel.dev");
17
17
  const CLI_ARGS = process.argv.slice(2);
18
+ function hasAnyFlag(args, ...flags) {
19
+ return flags.some((flag) => args.includes(flag));
20
+ }
21
+ const SHOW_HELP = hasAnyFlag(CLI_ARGS, "--help", "-h");
22
+ const DEBUG_MODE = hasAnyFlag(CLI_ARGS, "--debug", "-d");
23
+ if (DEBUG_MODE) {
24
+ process.env.LUNEL_DEBUG = "1";
25
+ process.env.LUNEL_DEBUG_AI = "1";
26
+ }
18
27
  const APPLE_REVIEW_CODE = "abcd";
19
28
  import { createRequire } from "module";
20
29
  const __require = createRequire(import.meta.url);
@@ -62,10 +71,35 @@ let shuttingDown = false;
62
71
  let activeControlWs = null;
63
72
  let activeDataWs = null;
64
73
  function logWithTimestamp(scope, message, fields) {
74
+ if (!DEBUG_MODE)
75
+ return;
65
76
  const timestamp = new Date().toISOString();
66
77
  const suffix = fields ? ` ${JSON.stringify(fields)}` : "";
67
78
  console.log(`[${timestamp}] [${scope}] ${message}${suffix}`);
68
79
  }
80
+ function debugLog(message, ...args) {
81
+ if (!DEBUG_MODE)
82
+ return;
83
+ console.log(message, ...args);
84
+ }
85
+ function debugWarn(message, ...args) {
86
+ if (!DEBUG_MODE)
87
+ return;
88
+ console.warn(message, ...args);
89
+ }
90
+ function printHelp() {
91
+ console.log(`Lunel CLI v${VERSION}
92
+
93
+ Usage:
94
+ npx lunel-cli [options]
95
+
96
+ Options:
97
+ -h, --help Show help
98
+ -n, --new Create a new session code
99
+ -d, --debug Show verbose debug logs
100
+ --extra-ports Extra local ports to expose, comma-separated (e.g. 3000,8080)
101
+ `);
102
+ }
69
103
  const activeTunnels = new Map();
70
104
  const PORT_SYNC_INTERVAL_MS = 30_000;
71
105
  const CLI_LOCAL_TCP_CONNECT_TIMEOUT_MS = 2_500;
@@ -211,12 +245,9 @@ function parseExtraPortsFromArgs(args) {
211
245
  }
212
246
  return Array.from(parsed).sort((a, b) => a - b);
213
247
  }
214
- function hasFlag(args, flag) {
215
- return args.includes(flag);
216
- }
217
248
  const EXTRA_PORTS = parseExtraPortsFromArgs(CLI_ARGS);
218
- const USE_APPLE_REVIEW_CODE = hasFlag(CLI_ARGS, "--abcd-code");
219
- const FORCE_NEW_CODE = hasFlag(CLI_ARGS, "--new-code");
249
+ const USE_APPLE_REVIEW_CODE = hasAnyFlag(CLI_ARGS, "--abcd-code");
250
+ const FORCE_NEW_CODE = hasAnyFlag(CLI_ARGS, "--new", "-n");
220
251
  const SCAN_PORTS = Array.from(new Set([...DEV_PORTS, ...EXTRA_PORTS])).sort((a, b) => a - b);
221
252
  function samePortSet(a, b) {
222
253
  if (a.length !== b.length)
@@ -953,14 +984,14 @@ function e2eeHandlePeerHello(peerPubkeyB64, dataWs) {
953
984
  e2eeSentReady = true;
954
985
  if (e2eeGotReady) {
955
986
  e2eeActive = true;
956
- console.log("[e2ee] encryption active");
987
+ debugLog("[e2ee] encryption active");
957
988
  }
958
989
  }
959
990
  function e2eeHandlePeerReady() {
960
991
  e2eeGotReady = true;
961
992
  if (e2eeSentReady) {
962
993
  e2eeActive = true;
963
- console.log("[e2ee] encryption active");
994
+ debugLog("[e2ee] encryption active");
964
995
  }
965
996
  }
966
997
  function e2eeEncrypt(payload) {
@@ -1055,7 +1086,7 @@ function resumeDataChannel() {
1055
1086
  lastSentSeq = entry.seq;
1056
1087
  }
1057
1088
  }
1058
- console.log(`[backpressure] data channel resumed, flushed ${toFlush.length} buffered events`);
1089
+ debugLog(`[backpressure] data channel resumed, flushed ${toFlush.length} buffered events`);
1059
1090
  if (activeControlWs?.readyState === WebSocket.OPEN) {
1060
1091
  const buffered = dataChannel?.bufferedAmount ?? 0;
1061
1092
  activeControlWs.send(JSON.stringify({ type: "data_channel_resumed", bufferedBytes: buffered }));
@@ -1067,7 +1098,7 @@ function checkDataChannelBackpressure() {
1067
1098
  const buffered = dataChannel.bufferedAmount ?? 0;
1068
1099
  if (!dataChannelPaused && buffered > DATA_CHANNEL_HIGH_WATER_BYTES) {
1069
1100
  dataChannelPaused = true;
1070
- console.warn(`[backpressure] data channel paused (${buffered} bytes buffered)`);
1101
+ debugWarn(`[backpressure] data channel paused (${buffered} bytes buffered)`);
1071
1102
  if (activeControlWs?.readyState === WebSocket.OPEN) {
1072
1103
  activeControlWs.send(JSON.stringify({ type: "data_channel_paused", bufferedBytes: buffered }));
1073
1104
  }
@@ -1240,7 +1271,7 @@ async function ensurePtyProcess() {
1240
1271
  console.error("[pty]", data.toString().trim());
1241
1272
  });
1242
1273
  ptyProcess.on("exit", (code) => {
1243
- console.log(`[pty] PTY process exited with code ${code}`);
1274
+ debugLog(`[pty] PTY process exited with code ${code}`);
1244
1275
  ptyProcess = null;
1245
1276
  // Reject all pending spawns
1246
1277
  for (const [id, pending] of ptyPendingSpawns) {
@@ -1941,7 +1972,7 @@ async function publishDiscoveredPorts(ws, force = false) {
1941
1972
  action: "ports_discovered",
1942
1973
  payload: { ports: openPorts },
1943
1974
  }));
1944
- console.log(`[proxy] ports updated (${openPorts.length}): ${openPorts.join(", ") || "-"}`);
1975
+ debugLog(`[proxy] ports updated (${openPorts.length}): ${openPorts.join(", ") || "-"}`);
1945
1976
  }
1946
1977
  catch (err) {
1947
1978
  console.error("Port scan failed:", err);
@@ -2236,7 +2267,7 @@ async function processMessage(message) {
2236
2267
  }
2237
2268
  // Validate required fields
2238
2269
  if (!ns || !action) {
2239
- console.warn("[router] Ignoring message with missing ns/action:", redactSensitive(JSON.stringify(message).substring(0, 300)));
2270
+ debugWarn("[router] Ignoring message with missing ns/action:", redactSensitive(JSON.stringify(message).substring(0, 300)));
2240
2271
  return {
2241
2272
  v: 1,
2242
2273
  id,
@@ -2519,7 +2550,9 @@ async function processMessage(message) {
2519
2550
  }
2520
2551
  catch (error) {
2521
2552
  const err = error;
2522
- console.error(`[router] ${ns}.${action} error:`, err.code || "ERROR", err.message);
2553
+ if (DEBUG_MODE) {
2554
+ console.error(`[router] ${ns}.${action} error:`, err.code || "ERROR", err.message);
2555
+ }
2523
2556
  logWithTimestamp("router", "request failed", {
2524
2557
  id,
2525
2558
  ns,
@@ -2659,7 +2692,7 @@ async function getAssignedProxyUrl(password) {
2659
2692
  }
2660
2693
  return normalizeGatewayUrl(payload.proxyUrl);
2661
2694
  }
2662
- async function revokePassword(password, reason = "revoked by cli --new-code") {
2695
+ async function revokePassword(password, reason = "revoked by cli --new") {
2663
2696
  const response = await fetch(new URL("/v2/revoke", MANAGER_URL), {
2664
2697
  method: "POST",
2665
2698
  headers: { "Content-Type": "application/json" },
@@ -2819,10 +2852,11 @@ async function connectWebSocket() {
2819
2852
  sendResponseOnData(response, dataWs);
2820
2853
  return;
2821
2854
  }
2822
- console.warn("[router] Ignoring non-request control frame");
2855
+ debugWarn("[router] Ignoring non-request control frame");
2823
2856
  }
2824
2857
  catch (error) {
2825
- console.error("Error processing control message:", error);
2858
+ if (DEBUG_MODE)
2859
+ console.error("Error processing control message:", error);
2826
2860
  }
2827
2861
  });
2828
2862
  controlWs.on("close", (code, reason) => {
@@ -2837,7 +2871,8 @@ async function connectWebSocket() {
2837
2871
  failConnection(`control ws error: ${error.message}`);
2838
2872
  return;
2839
2873
  }
2840
- console.error("Control WebSocket error:", error.message);
2874
+ if (DEBUG_MODE)
2875
+ console.error("Control WebSocket error:", error.message);
2841
2876
  });
2842
2877
  dataWs.on("open", () => {
2843
2878
  dataConnected = true;
@@ -2862,7 +2897,7 @@ async function connectWebSocket() {
2862
2897
  e2eeReset();
2863
2898
  const lastSeq = Number(raw.payload?.lastSeq ?? 0);
2864
2899
  const toReplay = replayBuffer.filter((e) => e.seq > lastSeq);
2865
- console.log(`[replay] replaying ${toReplay.length} messages after seq ${lastSeq}`);
2900
+ debugLog(`[replay] replaying ${toReplay.length} messages after seq ${lastSeq}`);
2866
2901
  // Replay without encryption — E2EE handshake hasn't completed yet
2867
2902
  for (const entry of toReplay)
2868
2903
  dataWs.send(JSON.stringify(entry.msg));
@@ -2889,10 +2924,11 @@ async function connectWebSocket() {
2889
2924
  sendResponseOnData(response, dataWs);
2890
2925
  return;
2891
2926
  }
2892
- console.warn("[router] Ignoring non-request data frame");
2927
+ debugWarn("[router] Ignoring non-request data frame");
2893
2928
  }
2894
2929
  catch (error) {
2895
- console.error("Error processing data message:", error);
2930
+ if (DEBUG_MODE)
2931
+ console.error("Error processing data message:", error);
2896
2932
  }
2897
2933
  });
2898
2934
  dataWs.on("close", (code, reason) => {
@@ -2913,7 +2949,8 @@ async function connectWebSocket() {
2913
2949
  failConnection(`data ws error: ${error.message}`);
2914
2950
  return;
2915
2951
  }
2916
- console.error("Data WebSocket error:", error.message);
2952
+ if (DEBUG_MODE)
2953
+ console.error("Data WebSocket error:", error.message);
2917
2954
  });
2918
2955
  setTimeout(() => {
2919
2956
  if (!settled) {
@@ -2939,16 +2976,21 @@ async function handleConnectionDrop(reason) {
2939
2976
  try {
2940
2977
  currentPrimaryGateway = await getAssignedProxyUrl(currentSessionPassword);
2941
2978
  await connectWebSocket();
2942
- console.log(`[reconnect] connected via ${activeGatewayUrl}`);
2979
+ debugLog(`[reconnect] connected via ${activeGatewayUrl}`);
2943
2980
  return;
2944
2981
  }
2945
2982
  catch (err) {
2946
- console.error(`[reconnect] attempt ${attempt} failed: ${err.message}`);
2983
+ if (DEBUG_MODE)
2984
+ console.error(`[reconnect] attempt ${attempt} failed: ${err.message}`);
2947
2985
  await new Promise((resolve) => setTimeout(resolve, delayMs));
2948
2986
  }
2949
2987
  }
2950
2988
  }
2951
2989
  async function main() {
2990
+ if (SHOW_HELP) {
2991
+ printHelp();
2992
+ return;
2993
+ }
2952
2994
  console.log("Lunel CLI v" + VERSION);
2953
2995
  console.log("=".repeat(20) + "\n");
2954
2996
  if (EXTRA_PORTS.length > 0) {
@@ -2958,9 +3000,9 @@ async function main() {
2958
3000
  try {
2959
3001
  const cliConfig = await getCliConfig();
2960
3002
  const savedSession = getSavedSessionForRoot(cliConfig, ROOT_DIR);
2961
- console.log("Checking PTY runtime...");
3003
+ debugLog("Checking PTY runtime...");
2962
3004
  await ensurePtyBinaryReady();
2963
- console.log("PTY runtime ready.\n");
3005
+ debugLog("PTY runtime ready.\n");
2964
3006
  // Start both AI backends (OpenCode + Codex). Unavailable ones are skipped.
2965
3007
  aiManager = await createAiManager();
2966
3008
  // Wire provider events → mobile app data channel, tagged with backend name.
@@ -3020,7 +3062,7 @@ async function main() {
3020
3062
  }
3021
3063
  if (error instanceof Error) {
3022
3064
  console.error(`Error: ${error.message}`);
3023
- if (error.stack)
3065
+ if (DEBUG_MODE && error.stack)
3024
3066
  console.error(error.stack);
3025
3067
  }
3026
3068
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunel-cli",
3
- "version": "0.1.82",
3
+ "version": "0.1.83",
4
4
  "author": [
5
5
  {
6
6
  "name": "Soham Bharambe",