lunel-cli 0.1.79 → 0.1.81

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 +125 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ 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
+ const APPLE_REVIEW_CODE = "abcd";
18
19
  import { createRequire } from "module";
19
20
  const __require = createRequire(import.meta.url);
20
21
  const VERSION = __require("../package.json").version;
@@ -210,7 +211,12 @@ function parseExtraPortsFromArgs(args) {
210
211
  }
211
212
  return Array.from(parsed).sort((a, b) => a - b);
212
213
  }
214
+ function hasFlag(args, flag) {
215
+ return args.includes(flag);
216
+ }
213
217
  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");
214
220
  const SCAN_PORTS = Array.from(new Set([...DEV_PORTS, ...EXTRA_PORTS])).sort((a, b) => a - b);
215
221
  function samePortSet(a, b) {
216
222
  if (a.length !== b.length)
@@ -298,18 +304,31 @@ async function readCliConfig() {
298
304
  return {
299
305
  version: 1,
300
306
  deviceId: typeof parsed.deviceId === "string" && parsed.deviceId ? parsed.deviceId : generatePersistentSecret(32),
307
+ sessions: Array.isArray(parsed.sessions)
308
+ ? parsed.sessions.filter((entry) => (!!entry
309
+ && typeof entry.rootDir === "string"
310
+ && typeof entry.sessionPassword === "string"
311
+ && typeof entry.savedAt === "number")).map((entry) => ({
312
+ rootDir: entry.rootDir,
313
+ sessionCode: typeof entry.sessionCode === "string" ? entry.sessionCode : null,
314
+ sessionPassword: entry.sessionPassword,
315
+ savedAt: entry.savedAt,
316
+ }))
317
+ : [],
301
318
  };
302
319
  }
303
320
  catch {
304
321
  return {
305
322
  version: 1,
306
323
  deviceId: generatePersistentSecret(32),
324
+ sessions: [],
307
325
  };
308
326
  }
309
327
  }
310
328
  async function writeCliConfig(config) {
311
329
  await fs.mkdir(path.dirname(CLI_CONFIG_PATH), { recursive: true });
312
330
  await fs.writeFile(CLI_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
331
+ cliConfigPromise = Promise.resolve(config);
313
332
  }
314
333
  let cliConfigPromise = null;
315
334
  async function getCliConfig() {
@@ -318,6 +337,34 @@ async function getCliConfig() {
318
337
  }
319
338
  return await cliConfigPromise;
320
339
  }
340
+ function getSavedSessionForRoot(config, rootDir) {
341
+ const sessions = Array.isArray(config.sessions) ? config.sessions : [];
342
+ return sessions.find((entry) => entry.rootDir === rootDir) || null;
343
+ }
344
+ async function saveSessionForRoot(sessionCode, sessionPassword) {
345
+ const config = await getCliConfig();
346
+ const sessions = Array.isArray(config.sessions) ? [...config.sessions] : [];
347
+ const nextEntry = {
348
+ rootDir: ROOT_DIR,
349
+ sessionCode,
350
+ sessionPassword,
351
+ savedAt: Date.now(),
352
+ };
353
+ const deduped = sessions.filter((entry) => entry.rootDir !== ROOT_DIR);
354
+ deduped.unshift(nextEntry);
355
+ await writeCliConfig({
356
+ ...config,
357
+ sessions: deduped.slice(0, 100),
358
+ });
359
+ }
360
+ async function clearSavedSessionForRoot() {
361
+ const config = await getCliConfig();
362
+ const sessions = Array.isArray(config.sessions) ? config.sessions : [];
363
+ await writeCliConfig({
364
+ ...config,
365
+ sessions: sessions.filter((entry) => entry.rootDir !== ROOT_DIR),
366
+ });
367
+ }
321
368
  // ============================================================================
322
369
  // File System Handlers
323
370
  // ============================================================================
@@ -2591,7 +2638,20 @@ async function getAssignedProxyUrl(password) {
2591
2638
  url.searchParams.set("password", password);
2592
2639
  const response = await fetch(url);
2593
2640
  if (!response.ok) {
2594
- throw new Error(`Failed to get proxy from manager: ${response.status}`);
2641
+ let message = `Failed to get proxy from manager: ${response.status}`;
2642
+ try {
2643
+ const payload = await response.json();
2644
+ if (payload.error) {
2645
+ message = payload.error;
2646
+ }
2647
+ else if (payload.reason) {
2648
+ message = payload.reason;
2649
+ }
2650
+ }
2651
+ catch {
2652
+ // ignore parse failures and use the fallback message
2653
+ }
2654
+ throw new Error(message);
2595
2655
  }
2596
2656
  const payload = await response.json();
2597
2657
  if (typeof payload.proxyUrl !== "string" || !payload.proxyUrl) {
@@ -2599,6 +2659,30 @@ async function getAssignedProxyUrl(password) {
2599
2659
  }
2600
2660
  return normalizeGatewayUrl(payload.proxyUrl);
2601
2661
  }
2662
+ async function revokePassword(password, reason = "revoked by cli --new-code") {
2663
+ const response = await fetch(new URL("/v2/revoke", MANAGER_URL), {
2664
+ method: "POST",
2665
+ headers: { "Content-Type": "application/json" },
2666
+ body: JSON.stringify({ password, reason }),
2667
+ });
2668
+ if (response.ok || response.status === 404) {
2669
+ return;
2670
+ }
2671
+ let message = `Failed to revoke previous session: ${response.status}`;
2672
+ try {
2673
+ const payload = await response.json();
2674
+ if (payload.error) {
2675
+ message = payload.error;
2676
+ }
2677
+ else if (payload.reason) {
2678
+ message = payload.reason;
2679
+ }
2680
+ }
2681
+ catch {
2682
+ // ignore parse failures and use the fallback message
2683
+ }
2684
+ throw new Error(message);
2685
+ }
2602
2686
  function displayQR(code) {
2603
2687
  console.log("\n");
2604
2688
  qrcode.generate(code, { small: true }, (qr) => {
@@ -2870,8 +2954,10 @@ async function main() {
2870
2954
  if (EXTRA_PORTS.length > 0) {
2871
2955
  console.log(`Extra ports enabled: ${EXTRA_PORTS.join(", ")}`);
2872
2956
  }
2957
+ let usedSavedSession = false;
2873
2958
  try {
2874
- await getCliConfig();
2959
+ const cliConfig = await getCliConfig();
2960
+ const savedSession = getSavedSessionForRoot(cliConfig, ROOT_DIR);
2875
2961
  console.log("Checking PTY runtime...");
2876
2962
  await ensurePtyBinaryReady();
2877
2963
  console.log("PTY runtime ready.\n");
@@ -2890,18 +2976,48 @@ async function main() {
2890
2976
  checkDataChannelBackpressure();
2891
2977
  }
2892
2978
  });
2893
- const qr = await createQrCode();
2894
- currentSessionCode = qr.code;
2895
- displayQR(qr.code);
2896
- const assembled = await assembleWithCode(qr.code);
2979
+ let sessionCodeToUse = null;
2980
+ let sessionPasswordToUse;
2981
+ if (USE_APPLE_REVIEW_CODE) {
2982
+ console.log(`Using fixed review code: ${APPLE_REVIEW_CODE}`);
2983
+ const assembled = await assembleWithCode(APPLE_REVIEW_CODE);
2984
+ sessionCodeToUse = assembled.code;
2985
+ sessionPasswordToUse = assembled.password;
2986
+ await saveSessionForRoot(sessionCodeToUse, sessionPasswordToUse);
2987
+ }
2988
+ else if (!FORCE_NEW_CODE && savedSession) {
2989
+ console.log(`Using saved session for ${ROOT_DIR}`);
2990
+ sessionCodeToUse = savedSession.sessionCode;
2991
+ sessionPasswordToUse = savedSession.sessionPassword;
2992
+ usedSavedSession = true;
2993
+ }
2994
+ else {
2995
+ if (FORCE_NEW_CODE && savedSession?.sessionPassword) {
2996
+ await revokePassword(savedSession.sessionPassword);
2997
+ await clearSavedSessionForRoot();
2998
+ }
2999
+ const qr = await createQrCode();
3000
+ currentSessionCode = qr.code;
3001
+ displayQR(qr.code);
3002
+ const assembled = await assembleWithCode(qr.code);
3003
+ sessionCodeToUse = assembled.code;
3004
+ sessionPasswordToUse = assembled.password;
3005
+ await saveSessionForRoot(sessionCodeToUse, sessionPasswordToUse);
3006
+ }
2897
3007
  resetReplayBuffer();
2898
- currentSessionCode = assembled.code;
2899
- currentSessionPassword = assembled.password;
2900
- currentPrimaryGateway = await getAssignedProxyUrl(assembled.password);
3008
+ currentSessionCode = sessionCodeToUse;
3009
+ currentSessionPassword = sessionPasswordToUse;
3010
+ currentPrimaryGateway = await getAssignedProxyUrl(sessionPasswordToUse);
2901
3011
  activeGatewayUrl = currentPrimaryGateway;
2902
3012
  await connectWebSocket();
2903
3013
  }
2904
3014
  catch (error) {
3015
+ const message = error instanceof Error ? error.message : String(error);
3016
+ if (!USE_APPLE_REVIEW_CODE &&
3017
+ usedSavedSession &&
3018
+ /invalid|revoked|not found|expired|password invalid|password revoked/i.test(message)) {
3019
+ await clearSavedSessionForRoot().catch(() => { });
3020
+ }
2905
3021
  if (error instanceof Error) {
2906
3022
  console.error(`Error: ${error.message}`);
2907
3023
  if (error.stack)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunel-cli",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "author": [
5
5
  {
6
6
  "name": "Soham Bharambe",