rechrome 1.7.0 → 1.8.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.
- package/package.json +1 -1
- package/rech.js +42 -6
- package/rech.ts +42 -6
package/package.json
CHANGED
package/rech.js
CHANGED
|
@@ -118,7 +118,8 @@ export function parseUrl(raw: string) {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export async function getOrCreateUrl(): Promise<string> {
|
|
121
|
-
|
|
121
|
+
// Treat a URL without a bearer key as missing — it cannot authenticate
|
|
122
|
+
try { if (process.env[ENV_KEY] && new URL(process.env[ENV_KEY]!).username) return process.env[ENV_KEY]!; } catch {}
|
|
122
123
|
const key = randomBytes(9).toString("base64url"); // 12 chars
|
|
123
124
|
const url = `http://${key}@127.0.0.1:${DEFAULT_PORT}`;
|
|
124
125
|
const newLine = `${ENV_KEY}=${url}`;
|
|
@@ -338,14 +339,38 @@ async function callServe(
|
|
|
338
339
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
|
|
339
340
|
body: JSON.stringify({ args, identity, env }),
|
|
340
341
|
signal: AbortSignal.timeout(70_000),
|
|
341
|
-
}).catch((e) => {
|
|
342
|
-
|
|
342
|
+
}).catch(async (e) => {
|
|
343
|
+
console.error(`[rech] ${e.message}`);
|
|
344
|
+
const dnsResult = await import("dns/promises").then(m => m.lookup(host)).catch(() => null);
|
|
345
|
+
if (!dnsResult) {
|
|
346
|
+
console.error(`[rech] rech-client\n -x: DNS failed -> ${host}[unknown] -> rech-server[unknown]`);
|
|
347
|
+
} else {
|
|
348
|
+
const tcpOk = await new Promise<boolean>(resolve => {
|
|
349
|
+
import("net").then(({ createConnection }) => {
|
|
350
|
+
const s = createConnection({ host, port: Number(port), timeout: 3000 });
|
|
351
|
+
s.on("connect", () => { s.destroy(); resolve(true); });
|
|
352
|
+
s.on("error", () => resolve(false));
|
|
353
|
+
s.on("timeout", () => { s.destroy(); resolve(false); });
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
if (tcpOk) {
|
|
357
|
+
console.error(`[rech] rech-client -> ${host}:${port}\n -x: connection refused -> rech-server[unknown]`);
|
|
358
|
+
} else {
|
|
359
|
+
console.error(`[rech] rech-client -> ${host}(${dnsResult.address})\n -x: port ${port} unreachable -> rech-server[unknown]`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
process.exit(1);
|
|
363
|
+
});
|
|
364
|
+
if (res.status === 401) {
|
|
365
|
+
console.error(`[rech] rech-client -> rech-server[ok]\n -x: token rejected -> playwright[unknown]`);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
343
368
|
return res.json();
|
|
344
369
|
}
|
|
345
370
|
|
|
346
371
|
async function run(url: string, args: string[]) {
|
|
347
|
-
const { host, port, protocol } = parseUrl(url);
|
|
348
|
-
const effectiveProfile =
|
|
372
|
+
const { host, port, protocol, extensionId, extensionToken, profileDirectory, userDataDir } = parseUrl(url);
|
|
373
|
+
const effectiveProfile = profileDirectory || process.env.PLAYWRIGHT_MCP_PROFILE_DIRECTORY;
|
|
349
374
|
const displayProfile = effectiveProfile ? await resolveProfileEmail(effectiveProfile) : undefined;
|
|
350
375
|
const identity = await getClientIdentity();
|
|
351
376
|
const profileSuffix = displayProfile ? ` profile:${displayProfile}` : "";
|
|
@@ -353,11 +378,21 @@ async function run(url: string, args: string[]) {
|
|
|
353
378
|
`[rech] connecting to ${host}:${port} (identity: ${identity.gitUrl || `${identity.hostname}:${identity.cwd}`}${profileSuffix})`,
|
|
354
379
|
);
|
|
355
380
|
|
|
381
|
+
const resolvedEnv = await getClientEnv({ extensionId, extensionToken, profileDirectory, userDataDir });
|
|
356
382
|
const { status, stdout, stderr, files, existingSession } = await callServe(url, args);
|
|
357
383
|
|
|
358
384
|
if (existingSession)
|
|
359
385
|
console.error(`[rech] session already has open tabs — listing existing tabs instead of opening a new window`);
|
|
360
|
-
if (stderr)
|
|
386
|
+
if (stderr) {
|
|
387
|
+
if (stderr.includes('Extension connection timeout')) {
|
|
388
|
+
const hasToken = !!resolvedEnv["PLAYWRIGHT_MCP_EXTENSION_TOKEN"];
|
|
389
|
+
const last = hasToken
|
|
390
|
+
? ` -x: extension token rejected -> extension[unknown]`
|
|
391
|
+
: ` -> extension[not installed] (run: rech setup)`;
|
|
392
|
+
console.error(`[rech] rech-client -> rech-server[ok] -> playwright[ok]\n${last}`);
|
|
393
|
+
}
|
|
394
|
+
process.stderr.write(stderr);
|
|
395
|
+
}
|
|
361
396
|
if (stdout) process.stdout.write(stdout);
|
|
362
397
|
|
|
363
398
|
if (files?.length) {
|
|
@@ -611,6 +646,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
|
|
|
611
646
|
if (saveChoice === "2") mkdirSync(join(process.cwd(), ".rechrome"), { recursive: true });
|
|
612
647
|
const existing = await file(globalEnvPath).text().catch(() => "");
|
|
613
648
|
const rechUrl = new URL(url);
|
|
649
|
+
if (!rechUrl.username) rechUrl.username = randomBytes(9).toString("base64url");
|
|
614
650
|
rechUrl.searchParams.set("extension_id", extId);
|
|
615
651
|
rechUrl.searchParams.set("token", token);
|
|
616
652
|
rechUrl.searchParams.set("profile", profileEmail);
|
package/rech.ts
CHANGED
|
@@ -118,7 +118,8 @@ export function parseUrl(raw: string) {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export async function getOrCreateUrl(): Promise<string> {
|
|
121
|
-
|
|
121
|
+
// Treat a URL without a bearer key as missing — it cannot authenticate
|
|
122
|
+
try { if (process.env[ENV_KEY] && new URL(process.env[ENV_KEY]!).username) return process.env[ENV_KEY]!; } catch {}
|
|
122
123
|
const key = randomBytes(9).toString("base64url"); // 12 chars
|
|
123
124
|
const url = `http://${key}@127.0.0.1:${DEFAULT_PORT}`;
|
|
124
125
|
const newLine = `${ENV_KEY}=${url}`;
|
|
@@ -338,14 +339,38 @@ async function callServe(
|
|
|
338
339
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
|
|
339
340
|
body: JSON.stringify({ args, identity, env }),
|
|
340
341
|
signal: AbortSignal.timeout(70_000),
|
|
341
|
-
}).catch((e) => {
|
|
342
|
-
|
|
342
|
+
}).catch(async (e) => {
|
|
343
|
+
console.error(`[rech] ${e.message}`);
|
|
344
|
+
const dnsResult = await import("dns/promises").then(m => m.lookup(host)).catch(() => null);
|
|
345
|
+
if (!dnsResult) {
|
|
346
|
+
console.error(`[rech] rech-client\n -x: DNS failed -> ${host}[unknown] -> rech-server[unknown]`);
|
|
347
|
+
} else {
|
|
348
|
+
const tcpOk = await new Promise<boolean>(resolve => {
|
|
349
|
+
import("net").then(({ createConnection }) => {
|
|
350
|
+
const s = createConnection({ host, port: Number(port), timeout: 3000 });
|
|
351
|
+
s.on("connect", () => { s.destroy(); resolve(true); });
|
|
352
|
+
s.on("error", () => resolve(false));
|
|
353
|
+
s.on("timeout", () => { s.destroy(); resolve(false); });
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
if (tcpOk) {
|
|
357
|
+
console.error(`[rech] rech-client -> ${host}:${port}\n -x: connection refused -> rech-server[unknown]`);
|
|
358
|
+
} else {
|
|
359
|
+
console.error(`[rech] rech-client -> ${host}(${dnsResult.address})\n -x: port ${port} unreachable -> rech-server[unknown]`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
process.exit(1);
|
|
363
|
+
});
|
|
364
|
+
if (res.status === 401) {
|
|
365
|
+
console.error(`[rech] rech-client -> rech-server[ok]\n -x: token rejected -> playwright[unknown]`);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
343
368
|
return res.json();
|
|
344
369
|
}
|
|
345
370
|
|
|
346
371
|
async function run(url: string, args: string[]) {
|
|
347
|
-
const { host, port, protocol } = parseUrl(url);
|
|
348
|
-
const effectiveProfile =
|
|
372
|
+
const { host, port, protocol, extensionId, extensionToken, profileDirectory, userDataDir } = parseUrl(url);
|
|
373
|
+
const effectiveProfile = profileDirectory || process.env.PLAYWRIGHT_MCP_PROFILE_DIRECTORY;
|
|
349
374
|
const displayProfile = effectiveProfile ? await resolveProfileEmail(effectiveProfile) : undefined;
|
|
350
375
|
const identity = await getClientIdentity();
|
|
351
376
|
const profileSuffix = displayProfile ? ` profile:${displayProfile}` : "";
|
|
@@ -353,11 +378,21 @@ async function run(url: string, args: string[]) {
|
|
|
353
378
|
`[rech] connecting to ${host}:${port} (identity: ${identity.gitUrl || `${identity.hostname}:${identity.cwd}`}${profileSuffix})`,
|
|
354
379
|
);
|
|
355
380
|
|
|
381
|
+
const resolvedEnv = await getClientEnv({ extensionId, extensionToken, profileDirectory, userDataDir });
|
|
356
382
|
const { status, stdout, stderr, files, existingSession } = await callServe(url, args);
|
|
357
383
|
|
|
358
384
|
if (existingSession)
|
|
359
385
|
console.error(`[rech] session already has open tabs — listing existing tabs instead of opening a new window`);
|
|
360
|
-
if (stderr)
|
|
386
|
+
if (stderr) {
|
|
387
|
+
if (stderr.includes('Extension connection timeout')) {
|
|
388
|
+
const hasToken = !!resolvedEnv["PLAYWRIGHT_MCP_EXTENSION_TOKEN"];
|
|
389
|
+
const last = hasToken
|
|
390
|
+
? ` -x: extension token rejected -> extension[unknown]`
|
|
391
|
+
: ` -> extension[not installed] (run: rech setup)`;
|
|
392
|
+
console.error(`[rech] rech-client -> rech-server[ok] -> playwright[ok]\n${last}`);
|
|
393
|
+
}
|
|
394
|
+
process.stderr.write(stderr);
|
|
395
|
+
}
|
|
361
396
|
if (stdout) process.stdout.write(stdout);
|
|
362
397
|
|
|
363
398
|
if (files?.length) {
|
|
@@ -611,6 +646,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
|
|
|
611
646
|
if (saveChoice === "2") mkdirSync(join(process.cwd(), ".rechrome"), { recursive: true });
|
|
612
647
|
const existing = await file(globalEnvPath).text().catch(() => "");
|
|
613
648
|
const rechUrl = new URL(url);
|
|
649
|
+
if (!rechUrl.username) rechUrl.username = randomBytes(9).toString("base64url");
|
|
614
650
|
rechUrl.searchParams.set("extension_id", extId);
|
|
615
651
|
rechUrl.searchParams.set("token", token);
|
|
616
652
|
rechUrl.searchParams.set("profile", profileEmail);
|