rechrome 1.9.0 → 1.9.2

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 (3) hide show
  1. package/package.json +1 -1
  2. package/rech.js +39 -14
  3. package/rech.ts +39 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rechrome",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/snomiao/rechrome.git"
package/rech.js CHANGED
@@ -362,7 +362,7 @@ async function callServe(
362
362
  process.exit(1);
363
363
  });
364
364
  if (res.status === 401) {
365
- console.error(`[rech] rech-client -> rech-server[ok]\n -x: token rejected -> playwright[unknown]`);
365
+ console.error(`[rech] rech-client -> rech-server[ok]\n -x: token rejected (used: ${key.slice(0, 6)}...) -> playwright[unknown]`);
366
366
  process.exit(1);
367
367
  }
368
368
  return res.json();
@@ -517,10 +517,23 @@ async function daemonUninstall(): Promise<void> {
517
517
  async function setup(opts: { profile?: string } = {}): Promise<void> {
518
518
  const { createInterface } = await import("readline");
519
519
  const isTTY = process.stdin.isTTY ?? false;
520
- const rl = isTTY ? createInterface({ input: process.stdin, output: process.stdout }) : null;
520
+ let rl: ReturnType<typeof createInterface> | null = null;
521
+ let stdinQueue: string[] | null = null;
522
+ if (isTTY) {
523
+ rl = createInterface({ input: process.stdin, output: process.stdout });
524
+ } else {
525
+ // Pre-read all piped stdin lines so readline close doesn't block later prompts
526
+ stdinQueue = await new Promise<string[]>(resolve => {
527
+ const lines: string[] = [];
528
+ const r = createInterface({ input: process.stdin });
529
+ r.on("line", l => lines.push(l));
530
+ r.on("close", () => resolve(lines));
531
+ });
532
+ }
521
533
  const ask = (q: string, def = "") => {
522
- if (!rl) { process.stdout.write(`${q}${def}\n`); return Promise.resolve(def); }
523
- return new Promise<string>(r => rl.question(q, r));
534
+ process.stdout.write(q);
535
+ if (stdinQueue !== null) { const ans = stdinQueue.shift() ?? def; process.stdout.write(ans + "\n"); return Promise.resolve(ans); }
536
+ return new Promise<string>(r => rl!.question("", ans => r(ans || def)));
524
537
  };
525
538
 
526
539
  // [1/4] Daemon
@@ -536,13 +549,18 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
536
549
  const { host, port, protocol } = parseUrl(url);
537
550
 
538
551
  const { key: serveKey } = parseUrl(url);
539
- const authPing = await fetch(`${protocol}://${host}:${port}/ping`, {
552
+ // First check if server is up at all (unauthenticated root), then verify our key matches
553
+ const anonPing = await fetch(`${protocol}://${host}:${port}/`, { signal: AbortSignal.timeout(2000) }).catch(() => null);
554
+ const authPing = anonPing ? await fetch(`${protocol}://${host}:${port}/ping`, {
540
555
  headers: { Authorization: `Bearer ${serveKey}` },
541
556
  signal: AbortSignal.timeout(2000),
542
- }).catch(() => null);
543
- if (authPing?.ok) {
557
+ }).catch(() => null) : null;
558
+ if (anonPing && authPing?.ok) {
544
559
  console.log(` Already running at ${protocol}://${host}:${port} — skipping reinstall`);
545
560
  await runOxmgr(["service", "install"]);
561
+ } else if (anonPing && !authPing?.ok) {
562
+ console.log(` Server running but key mismatch — reinstalling with new key`);
563
+ await daemonInstall(url);
546
564
  } else {
547
565
  await daemonInstall(url);
548
566
  console.log(` Registered daemon: ${OXMGR_PROCESS_NAME}`);
@@ -611,13 +629,19 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
611
629
  const statusUrl = `chrome-extension://${extId}/status.html`;
612
630
  console.log(`\n Get auth token from the extension:`);
613
631
  console.log(` ${statusUrl}`);
614
- Bun.spawn(
615
- ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
616
- `--profile-directory=${profileDir}`, statusUrl],
617
- { stdout: "ignore", stderr: "ignore", detached: true },
618
- );
619
- console.log(`\n Or click the extension icon in the Chrome toolbar.`);
620
- console.log(` Copy the token shown on the page (PLAYWRIGHT_MCP_EXTENSION_TOKEN=...).\n`);
632
+ if (isTTY) {
633
+ Bun.spawn(
634
+ ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
635
+ `--profile-directory=${profileDir}`, statusUrl],
636
+ { stdout: "ignore", stderr: "ignore", detached: true },
637
+ );
638
+ console.log(`\n Or click the extension icon in the Chrome toolbar.`);
639
+ console.log(` Copy the token shown on the page (PLAYWRIGHT_MCP_EXTENSION_TOKEN=...).\n`);
640
+ } else {
641
+ console.log(`\n [agent] Open the URL above in Chrome (profile: ${profileDisplay})`);
642
+ console.log(` [agent] Find PLAYWRIGHT_MCP_EXTENSION_TOKEN=... on that page`);
643
+ console.log(` [agent] Provide the token value on next stdin line:\n`);
644
+ }
621
645
  const tokenInput = (await ask(" Paste token: ")).trim();
622
646
  const token = tokenInput.replace(/^.*?=/, "").trim();
623
647
  if (!token || token.length < 20) { console.error(" Invalid token (too short)"); return null; }
@@ -648,6 +672,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
648
672
  if (userDataDir) rechUrl.searchParams.set("user_data_dir", userDataDir);
649
673
  const newLine = `RECHROME_URL=${rechUrl.toString()}`;
650
674
  console.log(`\n[4/4] Your RECHROME_URL:\n\n ${newLine}\n`);
675
+ if (!isTTY) console.log(` [agent] Provide save destination on next stdin line: 1=cwd, 2=cwd rechrome-only, 3=home, 4=skip\n`);
651
676
 
652
677
  const pwdEnvPath = join(process.cwd(), ".env.local");
653
678
  const pwdRechPath = join(process.cwd(), ".rechrome", ".env.local");
package/rech.ts CHANGED
@@ -362,7 +362,7 @@ async function callServe(
362
362
  process.exit(1);
363
363
  });
364
364
  if (res.status === 401) {
365
- console.error(`[rech] rech-client -> rech-server[ok]\n -x: token rejected -> playwright[unknown]`);
365
+ console.error(`[rech] rech-client -> rech-server[ok]\n -x: token rejected (used: ${key.slice(0, 6)}...) -> playwright[unknown]`);
366
366
  process.exit(1);
367
367
  }
368
368
  return res.json();
@@ -517,10 +517,23 @@ async function daemonUninstall(): Promise<void> {
517
517
  async function setup(opts: { profile?: string } = {}): Promise<void> {
518
518
  const { createInterface } = await import("readline");
519
519
  const isTTY = process.stdin.isTTY ?? false;
520
- const rl = isTTY ? createInterface({ input: process.stdin, output: process.stdout }) : null;
520
+ let rl: ReturnType<typeof createInterface> | null = null;
521
+ let stdinQueue: string[] | null = null;
522
+ if (isTTY) {
523
+ rl = createInterface({ input: process.stdin, output: process.stdout });
524
+ } else {
525
+ // Pre-read all piped stdin lines so readline close doesn't block later prompts
526
+ stdinQueue = await new Promise<string[]>(resolve => {
527
+ const lines: string[] = [];
528
+ const r = createInterface({ input: process.stdin });
529
+ r.on("line", l => lines.push(l));
530
+ r.on("close", () => resolve(lines));
531
+ });
532
+ }
521
533
  const ask = (q: string, def = "") => {
522
- if (!rl) { process.stdout.write(`${q}${def}\n`); return Promise.resolve(def); }
523
- return new Promise<string>(r => rl.question(q, r));
534
+ process.stdout.write(q);
535
+ if (stdinQueue !== null) { const ans = stdinQueue.shift() ?? def; process.stdout.write(ans + "\n"); return Promise.resolve(ans); }
536
+ return new Promise<string>(r => rl!.question("", ans => r(ans || def)));
524
537
  };
525
538
 
526
539
  // [1/4] Daemon
@@ -536,13 +549,18 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
536
549
  const { host, port, protocol } = parseUrl(url);
537
550
 
538
551
  const { key: serveKey } = parseUrl(url);
539
- const authPing = await fetch(`${protocol}://${host}:${port}/ping`, {
552
+ // First check if server is up at all (unauthenticated root), then verify our key matches
553
+ const anonPing = await fetch(`${protocol}://${host}:${port}/`, { signal: AbortSignal.timeout(2000) }).catch(() => null);
554
+ const authPing = anonPing ? await fetch(`${protocol}://${host}:${port}/ping`, {
540
555
  headers: { Authorization: `Bearer ${serveKey}` },
541
556
  signal: AbortSignal.timeout(2000),
542
- }).catch(() => null);
543
- if (authPing?.ok) {
557
+ }).catch(() => null) : null;
558
+ if (anonPing && authPing?.ok) {
544
559
  console.log(` Already running at ${protocol}://${host}:${port} — skipping reinstall`);
545
560
  await runOxmgr(["service", "install"]);
561
+ } else if (anonPing && !authPing?.ok) {
562
+ console.log(` Server running but key mismatch — reinstalling with new key`);
563
+ await daemonInstall(url);
546
564
  } else {
547
565
  await daemonInstall(url);
548
566
  console.log(` Registered daemon: ${OXMGR_PROCESS_NAME}`);
@@ -611,13 +629,19 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
611
629
  const statusUrl = `chrome-extension://${extId}/status.html`;
612
630
  console.log(`\n Get auth token from the extension:`);
613
631
  console.log(` ${statusUrl}`);
614
- Bun.spawn(
615
- ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
616
- `--profile-directory=${profileDir}`, statusUrl],
617
- { stdout: "ignore", stderr: "ignore", detached: true },
618
- );
619
- console.log(`\n Or click the extension icon in the Chrome toolbar.`);
620
- console.log(` Copy the token shown on the page (PLAYWRIGHT_MCP_EXTENSION_TOKEN=...).\n`);
632
+ if (isTTY) {
633
+ Bun.spawn(
634
+ ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
635
+ `--profile-directory=${profileDir}`, statusUrl],
636
+ { stdout: "ignore", stderr: "ignore", detached: true },
637
+ );
638
+ console.log(`\n Or click the extension icon in the Chrome toolbar.`);
639
+ console.log(` Copy the token shown on the page (PLAYWRIGHT_MCP_EXTENSION_TOKEN=...).\n`);
640
+ } else {
641
+ console.log(`\n [agent] Open the URL above in Chrome (profile: ${profileDisplay})`);
642
+ console.log(` [agent] Find PLAYWRIGHT_MCP_EXTENSION_TOKEN=... on that page`);
643
+ console.log(` [agent] Provide the token value on next stdin line:\n`);
644
+ }
621
645
  const tokenInput = (await ask(" Paste token: ")).trim();
622
646
  const token = tokenInput.replace(/^.*?=/, "").trim();
623
647
  if (!token || token.length < 20) { console.error(" Invalid token (too short)"); return null; }
@@ -648,6 +672,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
648
672
  if (userDataDir) rechUrl.searchParams.set("user_data_dir", userDataDir);
649
673
  const newLine = `RECHROME_URL=${rechUrl.toString()}`;
650
674
  console.log(`\n[4/4] Your RECHROME_URL:\n\n ${newLine}\n`);
675
+ if (!isTTY) console.log(` [agent] Provide save destination on next stdin line: 1=cwd, 2=cwd rechrome-only, 3=home, 4=skip\n`);
651
676
 
652
677
  const pwdEnvPath = join(process.cwd(), ".env.local");
653
678
  const pwdRechPath = join(process.cwd(), ".rechrome", ".env.local");