rechrome 1.8.2 → 1.9.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 (3) hide show
  1. package/package.json +1 -1
  2. package/rech.js +51 -28
  3. package/rech.ts +51 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rechrome",
3
- "version": "1.8.2",
3
+ "version": "1.9.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/snomiao/rechrome.git"
package/rech.js CHANGED
@@ -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
@@ -611,13 +624,19 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
611
624
  const statusUrl = `chrome-extension://${extId}/status.html`;
612
625
  console.log(`\n Get auth token from the extension:`);
613
626
  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`);
627
+ if (isTTY) {
628
+ Bun.spawn(
629
+ ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
630
+ `--profile-directory=${profileDir}`, statusUrl],
631
+ { stdout: "ignore", stderr: "ignore", detached: true },
632
+ );
633
+ console.log(`\n Or click the extension icon in the Chrome toolbar.`);
634
+ console.log(` Copy the token shown on the page (PLAYWRIGHT_MCP_EXTENSION_TOKEN=...).\n`);
635
+ } else {
636
+ console.log(`\n [agent] Open the URL above in Chrome (profile: ${profileDisplay})`);
637
+ console.log(` [agent] Find PLAYWRIGHT_MCP_EXTENSION_TOKEN=... on that page`);
638
+ console.log(` [agent] Provide the token value on next stdin line:\n`);
639
+ }
621
640
  const tokenInput = (await ask(" Paste token: ")).trim();
622
641
  const token = tokenInput.replace(/^.*?=/, "").trim();
623
642
  if (!token || token.length < 20) { console.error(" Invalid token (too short)"); return null; }
@@ -639,16 +658,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
639
658
  const { extId, token } = primary;
640
659
  const profileEmail = profileInfoSel.user_name || profileDir;
641
660
 
642
- // Save RECHROME_URL
643
- const pwdEnvPath = join(process.cwd(), ".env.local");
644
- const pwdRechPath = join(process.cwd(), ".rechrome", ".env.local");
645
- const homeEnvPath = join(process.env.HOME!, ".env.local");
646
- const saveChoice = (await ask(
647
- `\n[4/4] Save RECHROME_URL to:\n 1. ${pwdEnvPath} (current dir) [default]\n 2. ${pwdRechPath} (current dir, rechrome-only)\n 3. ${homeEnvPath} (user home)\n\n Choice [1]: `
648
- )).trim();
649
- const globalEnvPath = saveChoice === "3" ? homeEnvPath : saveChoice === "2" ? pwdRechPath : pwdEnvPath;
650
- if (saveChoice === "2") mkdirSync(join(process.cwd(), ".rechrome"), { recursive: true });
651
- const existing = await file(globalEnvPath).text().catch(() => "");
661
+ // Build RECHROME_URL and show it before asking where to save
652
662
  const rechUrl = new URL(url);
653
663
  if (!rechUrl.username) rechUrl.username = randomBytes(9).toString("base64url");
654
664
  rechUrl.searchParams.set("extension_id", extId);
@@ -656,14 +666,27 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
656
666
  rechUrl.searchParams.set("profile", profileEmail);
657
667
  if (userDataDir) rechUrl.searchParams.set("user_data_dir", userDataDir);
658
668
  const newLine = `RECHROME_URL=${rechUrl.toString()}`;
659
- const keysToRemove = ["PLAYWRIGHT_MCP_USER_DATA_DIR", "PLAYWRIGHT_MCP_EXTENSION_ID", "PLAYWRIGHT_MCP_EXTENSION_TOKEN", "PLAYWRIGHT_MCP_PROFILE_DIRECTORY"];
660
- let lines = existing.trimEnd().split("\n").filter(l => !keysToRemove.some(k => l.startsWith(`${k}=`)));
661
- const rechIdx = lines.findIndex(l => l.startsWith("RECHROME_URL="));
662
- if (rechIdx >= 0) lines[rechIdx] = newLine;
663
- else lines.push(newLine);
664
- await Bun.write(globalEnvPath, lines.join("\n").trim() + "\n");
665
- console.log(`\nSaved to ${globalEnvPath}`);
666
- console.log(`\n ${newLine}`);
669
+ console.log(`\n[4/4] Your RECHROME_URL:\n\n ${newLine}\n`);
670
+ if (!isTTY) console.log(` [agent] Provide save destination on next stdin line: 1=cwd, 2=cwd rechrome-only, 3=home, 4=skip\n`);
671
+
672
+ const pwdEnvPath = join(process.cwd(), ".env.local");
673
+ const pwdRechPath = join(process.cwd(), ".rechrome", ".env.local");
674
+ const homeEnvPath = join(process.env.HOME!, ".env.local");
675
+ const saveChoice = (await ask(
676
+ `Save to:\n 1. ${pwdEnvPath} (current dir) [default]\n 2. ${pwdRechPath} (current dir, rechrome-only)\n 3. ${homeEnvPath} (user home)\n 4. Skip (already copied)\n\n Choice [1]: `
677
+ )).trim();
678
+ if (saveChoice !== "4") {
679
+ const globalEnvPath = saveChoice === "3" ? homeEnvPath : saveChoice === "2" ? pwdRechPath : pwdEnvPath;
680
+ if (saveChoice === "2") mkdirSync(join(process.cwd(), ".rechrome"), { recursive: true });
681
+ const existing = await file(globalEnvPath).text().catch(() => "");
682
+ const keysToRemove = ["PLAYWRIGHT_MCP_USER_DATA_DIR", "PLAYWRIGHT_MCP_EXTENSION_ID", "PLAYWRIGHT_MCP_EXTENSION_TOKEN", "PLAYWRIGHT_MCP_PROFILE_DIRECTORY"];
683
+ let lines = existing.trimEnd().split("\n").filter(l => !keysToRemove.some(k => l.startsWith(`${k}=`)));
684
+ const rechIdx = lines.findIndex(l => l.startsWith("RECHROME_URL="));
685
+ if (rechIdx >= 0) lines[rechIdx] = newLine;
686
+ else lines.push(newLine);
687
+ await Bun.write(globalEnvPath, lines.join("\n").trim() + "\n");
688
+ console.log(`\nSaved to ${globalEnvPath}`);
689
+ }
667
690
 
668
691
  // Save primary to token registry
669
692
  await saveTokenEntry(profileEmail, { extensionId: extId, token, profileDir, userDataDir: userDataDir ?? undefined });
package/rech.ts CHANGED
@@ -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
@@ -611,13 +624,19 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
611
624
  const statusUrl = `chrome-extension://${extId}/status.html`;
612
625
  console.log(`\n Get auth token from the extension:`);
613
626
  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`);
627
+ if (isTTY) {
628
+ Bun.spawn(
629
+ ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
630
+ `--profile-directory=${profileDir}`, statusUrl],
631
+ { stdout: "ignore", stderr: "ignore", detached: true },
632
+ );
633
+ console.log(`\n Or click the extension icon in the Chrome toolbar.`);
634
+ console.log(` Copy the token shown on the page (PLAYWRIGHT_MCP_EXTENSION_TOKEN=...).\n`);
635
+ } else {
636
+ console.log(`\n [agent] Open the URL above in Chrome (profile: ${profileDisplay})`);
637
+ console.log(` [agent] Find PLAYWRIGHT_MCP_EXTENSION_TOKEN=... on that page`);
638
+ console.log(` [agent] Provide the token value on next stdin line:\n`);
639
+ }
621
640
  const tokenInput = (await ask(" Paste token: ")).trim();
622
641
  const token = tokenInput.replace(/^.*?=/, "").trim();
623
642
  if (!token || token.length < 20) { console.error(" Invalid token (too short)"); return null; }
@@ -639,16 +658,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
639
658
  const { extId, token } = primary;
640
659
  const profileEmail = profileInfoSel.user_name || profileDir;
641
660
 
642
- // Save RECHROME_URL
643
- const pwdEnvPath = join(process.cwd(), ".env.local");
644
- const pwdRechPath = join(process.cwd(), ".rechrome", ".env.local");
645
- const homeEnvPath = join(process.env.HOME!, ".env.local");
646
- const saveChoice = (await ask(
647
- `\n[4/4] Save RECHROME_URL to:\n 1. ${pwdEnvPath} (current dir) [default]\n 2. ${pwdRechPath} (current dir, rechrome-only)\n 3. ${homeEnvPath} (user home)\n\n Choice [1]: `
648
- )).trim();
649
- const globalEnvPath = saveChoice === "3" ? homeEnvPath : saveChoice === "2" ? pwdRechPath : pwdEnvPath;
650
- if (saveChoice === "2") mkdirSync(join(process.cwd(), ".rechrome"), { recursive: true });
651
- const existing = await file(globalEnvPath).text().catch(() => "");
661
+ // Build RECHROME_URL and show it before asking where to save
652
662
  const rechUrl = new URL(url);
653
663
  if (!rechUrl.username) rechUrl.username = randomBytes(9).toString("base64url");
654
664
  rechUrl.searchParams.set("extension_id", extId);
@@ -656,14 +666,27 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
656
666
  rechUrl.searchParams.set("profile", profileEmail);
657
667
  if (userDataDir) rechUrl.searchParams.set("user_data_dir", userDataDir);
658
668
  const newLine = `RECHROME_URL=${rechUrl.toString()}`;
659
- const keysToRemove = ["PLAYWRIGHT_MCP_USER_DATA_DIR", "PLAYWRIGHT_MCP_EXTENSION_ID", "PLAYWRIGHT_MCP_EXTENSION_TOKEN", "PLAYWRIGHT_MCP_PROFILE_DIRECTORY"];
660
- let lines = existing.trimEnd().split("\n").filter(l => !keysToRemove.some(k => l.startsWith(`${k}=`)));
661
- const rechIdx = lines.findIndex(l => l.startsWith("RECHROME_URL="));
662
- if (rechIdx >= 0) lines[rechIdx] = newLine;
663
- else lines.push(newLine);
664
- await Bun.write(globalEnvPath, lines.join("\n").trim() + "\n");
665
- console.log(`\nSaved to ${globalEnvPath}`);
666
- console.log(`\n ${newLine}`);
669
+ console.log(`\n[4/4] Your RECHROME_URL:\n\n ${newLine}\n`);
670
+ if (!isTTY) console.log(` [agent] Provide save destination on next stdin line: 1=cwd, 2=cwd rechrome-only, 3=home, 4=skip\n`);
671
+
672
+ const pwdEnvPath = join(process.cwd(), ".env.local");
673
+ const pwdRechPath = join(process.cwd(), ".rechrome", ".env.local");
674
+ const homeEnvPath = join(process.env.HOME!, ".env.local");
675
+ const saveChoice = (await ask(
676
+ `Save to:\n 1. ${pwdEnvPath} (current dir) [default]\n 2. ${pwdRechPath} (current dir, rechrome-only)\n 3. ${homeEnvPath} (user home)\n 4. Skip (already copied)\n\n Choice [1]: `
677
+ )).trim();
678
+ if (saveChoice !== "4") {
679
+ const globalEnvPath = saveChoice === "3" ? homeEnvPath : saveChoice === "2" ? pwdRechPath : pwdEnvPath;
680
+ if (saveChoice === "2") mkdirSync(join(process.cwd(), ".rechrome"), { recursive: true });
681
+ const existing = await file(globalEnvPath).text().catch(() => "");
682
+ const keysToRemove = ["PLAYWRIGHT_MCP_USER_DATA_DIR", "PLAYWRIGHT_MCP_EXTENSION_ID", "PLAYWRIGHT_MCP_EXTENSION_TOKEN", "PLAYWRIGHT_MCP_PROFILE_DIRECTORY"];
683
+ let lines = existing.trimEnd().split("\n").filter(l => !keysToRemove.some(k => l.startsWith(`${k}=`)));
684
+ const rechIdx = lines.findIndex(l => l.startsWith("RECHROME_URL="));
685
+ if (rechIdx >= 0) lines[rechIdx] = newLine;
686
+ else lines.push(newLine);
687
+ await Bun.write(globalEnvPath, lines.join("\n").trim() + "\n");
688
+ console.log(`\nSaved to ${globalEnvPath}`);
689
+ }
667
690
 
668
691
  // Save primary to token registry
669
692
  await saveTokenEntry(profileEmail, { extensionId: extId, token, profileDir, userDataDir: userDataDir ?? undefined });