rechrome 1.10.3 → 1.11.1

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 (5) hide show
  1. package/package.json +1 -1
  2. package/rech.js +60 -11
  3. package/rech.ts +60 -11
  4. package/serve.js +1 -1
  5. package/serve.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rechrome",
3
- "version": "1.10.3",
3
+ "version": "1.11.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/snomiao/rechrome.git"
package/rech.js CHANGED
@@ -554,6 +554,13 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
554
554
 
555
555
  // [1/4] Daemon
556
556
  console.log("\n[1/4] Setting up serve daemon...");
557
+
558
+ // Bind address (persists to ~/.env.local as RECH_HOST).
559
+ // Read the persisted value from ~/.env.local directly — process.env may be shadowed by nearer .env files.
560
+ const globalEnvRaw = await file(globalEnvFile).text().catch(() => "");
561
+ const persistedBindMatch = globalEnvRaw.match(/^\s*RECH_HOST\s*=\s*(.*?)\s*$/m);
562
+ const persistedBind = persistedBindMatch?.[1].replace(/^["']|["']$/g, "") || "127.0.0.1";
563
+
557
564
  // Clear stale hostname-based URL so we always use 127.0.0.1 locally
558
565
  if (process.env[ENV_KEY]) {
559
566
  try {
@@ -571,14 +578,35 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
571
578
  headers: { Authorization: `Bearer ${serveKey}` },
572
579
  signal: AbortSignal.timeout(2000),
573
580
  }).catch(() => null) : null;
574
- if (anonPing && authPing?.ok) {
575
- console.log(` Already running at ${protocol}://${host}:${port} — skipping reinstall`);
576
- } else if (anonPing && !authPing?.ok) {
577
- console.log(` Server running but key mismatch — reinstalling with new key`);
578
- await daemonInstall(url);
579
- } else {
580
- await daemonInstall(url);
581
- console.log(` Registered daemon: ${OXMGR_PROCESS_NAME}`);
581
+ // The daemon's *live* bind (from /ping) is authoritative — persisted RECH_HOST may diverge if the user edited it manually.
582
+ const liveBind = authPing?.ok
583
+ ? await authPing.clone().json().then((b: { bind?: string }) => b?.bind).catch(() => undefined)
584
+ : undefined;
585
+ // Pre-patch daemons return plain "ok" with no bind info — we can't trust persisted/env values to match their live bind, so force reinstall to be safe.
586
+ const liveBindUnknown = !!authPing?.ok && !liveBind;
587
+ const currentBind = liveBind || persistedBind;
588
+
589
+ // Non-TTY honors explicit process.env.RECH_HOST (shell or merged env stack) — matches the documented `RECH_HOST=0.0.0.0 rech setup` flow.
590
+ let desiredBind = process.env.RECH_HOST || currentBind;
591
+ if (isTTY) {
592
+ console.log(`\n Bind address (current: ${currentBind}):`);
593
+ console.log(` 1. 127.0.0.1 (localhost only)`);
594
+ console.log(` 2. 0.0.0.0 (all interfaces — HTTP plaintext, trust your network)`);
595
+ const defaultBindChoice = currentBind === "0.0.0.0" ? "2" : "1";
596
+ const bindAns = (await ask(` Choice [${defaultBindChoice}]: `, defaultBindChoice)).trim();
597
+ desiredBind = bindAns === "2" || bindAns === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1";
598
+ }
599
+ const bindChanged = desiredBind !== currentBind;
600
+ const persistedChanged = desiredBind !== persistedBind;
601
+ if (persistedChanged) {
602
+ const lines = globalEnvRaw.trimEnd().split("\n").filter(l => !/^\s*RECH_HOST\s*=/.test(l));
603
+ await Bun.write(globalEnvFile, [...lines, `RECH_HOST=${desiredBind}`, ""].join("\n"));
604
+ console.log(` Saved RECH_HOST=${desiredBind} to ~/.env.local`);
605
+ }
606
+ // Always align process.env with the desired bind — a nearer .env.local may have shadowed it.
607
+ process.env.RECH_HOST = desiredBind;
608
+
609
+ const waitForServe = async () => {
582
610
  process.stdout.write(" Starting");
583
611
  let ping = null;
584
612
  for (let i = 0; i < 15; i++) {
@@ -594,6 +622,26 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
594
622
  process.exit(1);
595
623
  }
596
624
  console.log(` Serve running at ${protocol}://${host}:${port}`);
625
+ };
626
+
627
+ if (anonPing && authPing?.ok && !bindChanged && !liveBindUnknown) {
628
+ console.log(` Already running at ${protocol}://${host}:${port} — skipping reinstall`);
629
+ } else if (anonPing && authPing?.ok && liveBindUnknown) {
630
+ console.log(` Pre-patch daemon detected (no live bind info) — reinstalling to verify bind`);
631
+ await daemonInstall(url);
632
+ await waitForServe();
633
+ } else if (anonPing && bindChanged) {
634
+ console.log(` Bind changed (${currentBind} → ${desiredBind}) — reinstalling`);
635
+ await daemonInstall(url);
636
+ await waitForServe();
637
+ } else if (anonPing && !authPing?.ok) {
638
+ console.log(` Server running but key mismatch — reinstalling with new key`);
639
+ await daemonInstall(url);
640
+ await waitForServe();
641
+ } else {
642
+ await daemonInstall(url);
643
+ console.log(` Registered daemon: ${OXMGR_PROCESS_NAME}`);
644
+ await waitForServe();
597
645
  }
598
646
 
599
647
  const cache = await readChromeProfileCache();
@@ -613,10 +661,11 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
613
661
  (info.user_name ?? "").toLowerCase().includes(opts.profile!.toLowerCase())
614
662
  ) ?? null;
615
663
  }
616
- if (!isTTY) {
617
- console.log(" Non-TTY: auto-selecting first profile");
618
- return available[0] ?? null;
664
+ if (available.length === 1) {
665
+ console.log(` Only one profile available — selecting: ${available[0][1].user_name || available[0][0]}`);
666
+ return available[0];
619
667
  }
668
+ if (!isTTY) console.log(" [agent] Provide profile number on next stdin line, or rerun with --profile <num|email>");
620
669
  const answer = await ask("\n Profile number: ");
621
670
  const idx = parseInt(answer.trim()) - 1;
622
671
  if (isNaN(idx) || idx < 0 || idx >= available.length) return null;
package/rech.ts CHANGED
@@ -554,6 +554,13 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
554
554
 
555
555
  // [1/4] Daemon
556
556
  console.log("\n[1/4] Setting up serve daemon...");
557
+
558
+ // Bind address (persists to ~/.env.local as RECH_HOST).
559
+ // Read the persisted value from ~/.env.local directly — process.env may be shadowed by nearer .env files.
560
+ const globalEnvRaw = await file(globalEnvFile).text().catch(() => "");
561
+ const persistedBindMatch = globalEnvRaw.match(/^\s*RECH_HOST\s*=\s*(.*?)\s*$/m);
562
+ const persistedBind = persistedBindMatch?.[1].replace(/^["']|["']$/g, "") || "127.0.0.1";
563
+
557
564
  // Clear stale hostname-based URL so we always use 127.0.0.1 locally
558
565
  if (process.env[ENV_KEY]) {
559
566
  try {
@@ -571,14 +578,35 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
571
578
  headers: { Authorization: `Bearer ${serveKey}` },
572
579
  signal: AbortSignal.timeout(2000),
573
580
  }).catch(() => null) : null;
574
- if (anonPing && authPing?.ok) {
575
- console.log(` Already running at ${protocol}://${host}:${port} — skipping reinstall`);
576
- } else if (anonPing && !authPing?.ok) {
577
- console.log(` Server running but key mismatch — reinstalling with new key`);
578
- await daemonInstall(url);
579
- } else {
580
- await daemonInstall(url);
581
- console.log(` Registered daemon: ${OXMGR_PROCESS_NAME}`);
581
+ // The daemon's *live* bind (from /ping) is authoritative — persisted RECH_HOST may diverge if the user edited it manually.
582
+ const liveBind = authPing?.ok
583
+ ? await authPing.clone().json().then((b: { bind?: string }) => b?.bind).catch(() => undefined)
584
+ : undefined;
585
+ // Pre-patch daemons return plain "ok" with no bind info — we can't trust persisted/env values to match their live bind, so force reinstall to be safe.
586
+ const liveBindUnknown = !!authPing?.ok && !liveBind;
587
+ const currentBind = liveBind || persistedBind;
588
+
589
+ // Non-TTY honors explicit process.env.RECH_HOST (shell or merged env stack) — matches the documented `RECH_HOST=0.0.0.0 rech setup` flow.
590
+ let desiredBind = process.env.RECH_HOST || currentBind;
591
+ if (isTTY) {
592
+ console.log(`\n Bind address (current: ${currentBind}):`);
593
+ console.log(` 1. 127.0.0.1 (localhost only)`);
594
+ console.log(` 2. 0.0.0.0 (all interfaces — HTTP plaintext, trust your network)`);
595
+ const defaultBindChoice = currentBind === "0.0.0.0" ? "2" : "1";
596
+ const bindAns = (await ask(` Choice [${defaultBindChoice}]: `, defaultBindChoice)).trim();
597
+ desiredBind = bindAns === "2" || bindAns === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1";
598
+ }
599
+ const bindChanged = desiredBind !== currentBind;
600
+ const persistedChanged = desiredBind !== persistedBind;
601
+ if (persistedChanged) {
602
+ const lines = globalEnvRaw.trimEnd().split("\n").filter(l => !/^\s*RECH_HOST\s*=/.test(l));
603
+ await Bun.write(globalEnvFile, [...lines, `RECH_HOST=${desiredBind}`, ""].join("\n"));
604
+ console.log(` Saved RECH_HOST=${desiredBind} to ~/.env.local`);
605
+ }
606
+ // Always align process.env with the desired bind — a nearer .env.local may have shadowed it.
607
+ process.env.RECH_HOST = desiredBind;
608
+
609
+ const waitForServe = async () => {
582
610
  process.stdout.write(" Starting");
583
611
  let ping = null;
584
612
  for (let i = 0; i < 15; i++) {
@@ -594,6 +622,26 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
594
622
  process.exit(1);
595
623
  }
596
624
  console.log(` Serve running at ${protocol}://${host}:${port}`);
625
+ };
626
+
627
+ if (anonPing && authPing?.ok && !bindChanged && !liveBindUnknown) {
628
+ console.log(` Already running at ${protocol}://${host}:${port} — skipping reinstall`);
629
+ } else if (anonPing && authPing?.ok && liveBindUnknown) {
630
+ console.log(` Pre-patch daemon detected (no live bind info) — reinstalling to verify bind`);
631
+ await daemonInstall(url);
632
+ await waitForServe();
633
+ } else if (anonPing && bindChanged) {
634
+ console.log(` Bind changed (${currentBind} → ${desiredBind}) — reinstalling`);
635
+ await daemonInstall(url);
636
+ await waitForServe();
637
+ } else if (anonPing && !authPing?.ok) {
638
+ console.log(` Server running but key mismatch — reinstalling with new key`);
639
+ await daemonInstall(url);
640
+ await waitForServe();
641
+ } else {
642
+ await daemonInstall(url);
643
+ console.log(` Registered daemon: ${OXMGR_PROCESS_NAME}`);
644
+ await waitForServe();
597
645
  }
598
646
 
599
647
  const cache = await readChromeProfileCache();
@@ -613,10 +661,11 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
613
661
  (info.user_name ?? "").toLowerCase().includes(opts.profile!.toLowerCase())
614
662
  ) ?? null;
615
663
  }
616
- if (!isTTY) {
617
- console.log(" Non-TTY: auto-selecting first profile");
618
- return available[0] ?? null;
664
+ if (available.length === 1) {
665
+ console.log(` Only one profile available — selecting: ${available[0][1].user_name || available[0][0]}`);
666
+ return available[0];
619
667
  }
668
+ if (!isTTY) console.log(" [agent] Provide profile number on next stdin line, or rerun with --profile <num|email>");
620
669
  const answer = await ask("\n Profile number: ");
621
670
  const idx = parseInt(answer.trim()) - 1;
622
671
  if (isNaN(idx) || idx < 0 || idx >= available.length) return null;
package/serve.js CHANGED
@@ -110,7 +110,7 @@ export async function serve() {
110
110
  if (reqUrl.pathname === "/ping") {
111
111
  const denied = authCheck(req, key);
112
112
  if (denied) return denied;
113
- return new Response("ok");
113
+ return Response.json({ ok: true, bind: listenHost });
114
114
  }
115
115
  if (reqUrl.pathname !== "/run") return new Response("rech server\n");
116
116
  const denied = authCheck(req, key);
package/serve.ts CHANGED
@@ -110,7 +110,7 @@ export async function serve() {
110
110
  if (reqUrl.pathname === "/ping") {
111
111
  const denied = authCheck(req, key);
112
112
  if (denied) return denied;
113
- return new Response("ok");
113
+ return Response.json({ ok: true, bind: listenHost });
114
114
  }
115
115
  if (reqUrl.pathname !== "/run") return new Response("rech server\n");
116
116
  const denied = authCheck(req, key);