rechrome 1.10.1 → 1.10.3
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 +20 -4
- package/rech.ts +20 -4
- package/serve.js +20 -13
- package/serve.ts +20 -13
package/package.json
CHANGED
package/rech.js
CHANGED
|
@@ -120,7 +120,7 @@ export function parseUrl(raw: string) {
|
|
|
120
120
|
export async function getOrCreateUrl(): Promise<string> {
|
|
121
121
|
// Treat a URL without a bearer key as missing — it cannot authenticate
|
|
122
122
|
try { if (process.env[ENV_KEY] && new URL(process.env[ENV_KEY]!).username) return process.env[ENV_KEY]!; } catch {}
|
|
123
|
-
const key = randomBytes(
|
|
123
|
+
const key = randomBytes(12).toString("base64url"); // 16 chars
|
|
124
124
|
const url = `http://${key}@127.0.0.1:${DEFAULT_PORT}`;
|
|
125
125
|
const newLine = `${ENV_KEY}=${url}`;
|
|
126
126
|
// Write to ~/.env.local so it's not shadowed by project .env.local
|
|
@@ -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:
|
|
365
|
+
console.error(`[rech] rech-client -> rech-server[ok]\n -x: bearer key rejected (used: ${key.slice(0, 4)}...) -> playwright[unknown]`);
|
|
366
366
|
process.exit(1);
|
|
367
367
|
}
|
|
368
368
|
return res.json();
|
|
@@ -381,6 +381,11 @@ async function run(url: string, args: string[]) {
|
|
|
381
381
|
const resolvedEnv = await getClientEnv({ extensionId, extensionToken, profileDirectory, userDataDir });
|
|
382
382
|
const { status, stdout, stderr, files, existingSession } = await callServe(url, args);
|
|
383
383
|
|
|
384
|
+
const isOpenWithUrl = args[0] === "open" && args.length > 1;
|
|
385
|
+
if (existingSession && isOpenWithUrl) {
|
|
386
|
+
return run(url, ["goto", ...args.slice(1)]);
|
|
387
|
+
}
|
|
388
|
+
|
|
384
389
|
if (existingSession)
|
|
385
390
|
console.error(`[rech] session already has open tabs — listing existing tabs instead of opening a new window`);
|
|
386
391
|
if (stderr) {
|
|
@@ -406,7 +411,7 @@ async function run(url: string, args: string[]) {
|
|
|
406
411
|
});
|
|
407
412
|
if (!fileRes.ok) continue;
|
|
408
413
|
const dest = join(dlDir, basename(name));
|
|
409
|
-
await Bun.write(dest, fileRes);
|
|
414
|
+
await Bun.write(dest, await fileRes.arrayBuffer());
|
|
410
415
|
console.error(`[rech] downloaded: ${dest}`);
|
|
411
416
|
}
|
|
412
417
|
}
|
|
@@ -481,6 +486,17 @@ async function runOxmgr(args: string[]): Promise<number> {
|
|
|
481
486
|
}
|
|
482
487
|
|
|
483
488
|
async function daemonInstall(serveUrl: string): Promise<void> {
|
|
489
|
+
// Persist the URL to ~/.env.local before starting the daemon. The daemon's
|
|
490
|
+
// loadEnv() walks CWD→root reading .env.local files and unconditionally
|
|
491
|
+
// overwrites process.env.RECHROME_URL from whichever file it finds first.
|
|
492
|
+
// Without this write, oxmgr's --env RECHROME_URL=... gets clobbered by a
|
|
493
|
+
// stale ~/.env.local entry — the daemon then listens on a different bearer
|
|
494
|
+
// key than the one daemonInstall was called with, and every client request
|
|
495
|
+
// is rejected with "bearer key rejected".
|
|
496
|
+
const envRaw = await file(globalEnvFile).text().catch(() => "");
|
|
497
|
+
const filtered = envRaw.trimEnd().split("\n").filter(l => !l.startsWith(`${ENV_KEY}=`));
|
|
498
|
+
await Bun.write(globalEnvFile, [...filtered, `${ENV_KEY}=${serveUrl}`, ""].join("\n"));
|
|
499
|
+
|
|
484
500
|
const home = process.env.HOME!;
|
|
485
501
|
const bunBin = Bun.which("bun") ?? process.execPath;
|
|
486
502
|
const rechScript = import.meta.filename;
|
|
@@ -677,7 +693,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
|
|
|
677
693
|
|
|
678
694
|
// Build RECHROME_URL and show it before asking where to save
|
|
679
695
|
const rechUrl = new URL(url);
|
|
680
|
-
if (!rechUrl.username) rechUrl.username = randomBytes(
|
|
696
|
+
if (!rechUrl.username) rechUrl.username = randomBytes(12).toString("base64url");
|
|
681
697
|
rechUrl.searchParams.set("extension_id", extId);
|
|
682
698
|
rechUrl.searchParams.set("token", token);
|
|
683
699
|
rechUrl.searchParams.set("profile", profileEmail);
|
package/rech.ts
CHANGED
|
@@ -120,7 +120,7 @@ export function parseUrl(raw: string) {
|
|
|
120
120
|
export async function getOrCreateUrl(): Promise<string> {
|
|
121
121
|
// Treat a URL without a bearer key as missing — it cannot authenticate
|
|
122
122
|
try { if (process.env[ENV_KEY] && new URL(process.env[ENV_KEY]!).username) return process.env[ENV_KEY]!; } catch {}
|
|
123
|
-
const key = randomBytes(
|
|
123
|
+
const key = randomBytes(12).toString("base64url"); // 16 chars
|
|
124
124
|
const url = `http://${key}@127.0.0.1:${DEFAULT_PORT}`;
|
|
125
125
|
const newLine = `${ENV_KEY}=${url}`;
|
|
126
126
|
// Write to ~/.env.local so it's not shadowed by project .env.local
|
|
@@ -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:
|
|
365
|
+
console.error(`[rech] rech-client -> rech-server[ok]\n -x: bearer key rejected (used: ${key.slice(0, 4)}...) -> playwright[unknown]`);
|
|
366
366
|
process.exit(1);
|
|
367
367
|
}
|
|
368
368
|
return res.json();
|
|
@@ -381,6 +381,11 @@ async function run(url: string, args: string[]) {
|
|
|
381
381
|
const resolvedEnv = await getClientEnv({ extensionId, extensionToken, profileDirectory, userDataDir });
|
|
382
382
|
const { status, stdout, stderr, files, existingSession } = await callServe(url, args);
|
|
383
383
|
|
|
384
|
+
const isOpenWithUrl = args[0] === "open" && args.length > 1;
|
|
385
|
+
if (existingSession && isOpenWithUrl) {
|
|
386
|
+
return run(url, ["goto", ...args.slice(1)]);
|
|
387
|
+
}
|
|
388
|
+
|
|
384
389
|
if (existingSession)
|
|
385
390
|
console.error(`[rech] session already has open tabs — listing existing tabs instead of opening a new window`);
|
|
386
391
|
if (stderr) {
|
|
@@ -406,7 +411,7 @@ async function run(url: string, args: string[]) {
|
|
|
406
411
|
});
|
|
407
412
|
if (!fileRes.ok) continue;
|
|
408
413
|
const dest = join(dlDir, basename(name));
|
|
409
|
-
await Bun.write(dest, fileRes);
|
|
414
|
+
await Bun.write(dest, await fileRes.arrayBuffer());
|
|
410
415
|
console.error(`[rech] downloaded: ${dest}`);
|
|
411
416
|
}
|
|
412
417
|
}
|
|
@@ -481,6 +486,17 @@ async function runOxmgr(args: string[]): Promise<number> {
|
|
|
481
486
|
}
|
|
482
487
|
|
|
483
488
|
async function daemonInstall(serveUrl: string): Promise<void> {
|
|
489
|
+
// Persist the URL to ~/.env.local before starting the daemon. The daemon's
|
|
490
|
+
// loadEnv() walks CWD→root reading .env.local files and unconditionally
|
|
491
|
+
// overwrites process.env.RECHROME_URL from whichever file it finds first.
|
|
492
|
+
// Without this write, oxmgr's --env RECHROME_URL=... gets clobbered by a
|
|
493
|
+
// stale ~/.env.local entry — the daemon then listens on a different bearer
|
|
494
|
+
// key than the one daemonInstall was called with, and every client request
|
|
495
|
+
// is rejected with "bearer key rejected".
|
|
496
|
+
const envRaw = await file(globalEnvFile).text().catch(() => "");
|
|
497
|
+
const filtered = envRaw.trimEnd().split("\n").filter(l => !l.startsWith(`${ENV_KEY}=`));
|
|
498
|
+
await Bun.write(globalEnvFile, [...filtered, `${ENV_KEY}=${serveUrl}`, ""].join("\n"));
|
|
499
|
+
|
|
484
500
|
const home = process.env.HOME!;
|
|
485
501
|
const bunBin = Bun.which("bun") ?? process.execPath;
|
|
486
502
|
const rechScript = import.meta.filename;
|
|
@@ -677,7 +693,7 @@ async function setup(opts: { profile?: string } = {}): Promise<void> {
|
|
|
677
693
|
|
|
678
694
|
// Build RECHROME_URL and show it before asking where to save
|
|
679
695
|
const rechUrl = new URL(url);
|
|
680
|
-
if (!rechUrl.username) rechUrl.username = randomBytes(
|
|
696
|
+
if (!rechUrl.username) rechUrl.username = randomBytes(12).toString("base64url");
|
|
681
697
|
rechUrl.searchParams.set("extension_id", extId);
|
|
682
698
|
rechUrl.searchParams.set("token", token);
|
|
683
699
|
rechUrl.searchParams.set("profile", profileEmail);
|
package/serve.js
CHANGED
|
@@ -173,8 +173,8 @@ export async function serve() {
|
|
|
173
173
|
|
|
174
174
|
// For open commands, default to about:blank to avoid leaving connect.html visible
|
|
175
175
|
const isOpenCmd = filteredArgs[0] === "open";
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
const isOpenNoUrl = isOpenCmd && filteredArgs.length === 1;
|
|
177
|
+
if (isOpenNoUrl) filteredArgs.push("about:blank");
|
|
178
178
|
|
|
179
179
|
// bare `rech open` with no URL: warn if session already has tabs
|
|
180
180
|
if (isOpenCmd && filteredArgs.length === 1) {
|
|
@@ -186,19 +186,26 @@ export async function serve() {
|
|
|
186
186
|
stderr: "pipe",
|
|
187
187
|
env: { PATH: process.env.PATH, HOME: process.env.HOME },
|
|
188
188
|
});
|
|
189
|
-
const [listStatus, listOut] = await Promise.
|
|
190
|
-
listProc.exited,
|
|
191
|
-
new
|
|
189
|
+
const [listStatus, listOut] = await Promise.race([
|
|
190
|
+
Promise.all([listProc.exited, new Response(listProc.stdout).text()]),
|
|
191
|
+
new Promise<[number, string]>((resolve) =>
|
|
192
|
+
setTimeout(() => { listProc.kill(); resolve([1, ""]); }, 5000)
|
|
193
|
+
),
|
|
192
194
|
]);
|
|
193
195
|
if (listStatus === 0 && listOut.trim()) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
if (isOpenNoUrl) {
|
|
197
|
+
log(`session ${namespacedSession} already has tabs, returning tab-list hint`);
|
|
198
|
+
return Response.json({
|
|
199
|
+
status: 0,
|
|
200
|
+
stdout: listOut,
|
|
201
|
+
stderr: `[rech] session "${namespacedSession}" already has open tabs:\n`,
|
|
202
|
+
files: [],
|
|
203
|
+
existingSession: true,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// URL specified: navigate to it instead of returning tab-list
|
|
207
|
+
log(`session ${namespacedSession} already has tabs, converting open to goto`);
|
|
208
|
+
filteredArgs[0] = "goto";
|
|
202
209
|
}
|
|
203
210
|
} catch (e) {
|
|
204
211
|
log(`tab-list check failed: ${e}`);
|
package/serve.ts
CHANGED
|
@@ -173,8 +173,8 @@ export async function serve() {
|
|
|
173
173
|
|
|
174
174
|
// For open commands, default to about:blank to avoid leaving connect.html visible
|
|
175
175
|
const isOpenCmd = filteredArgs[0] === "open";
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
const isOpenNoUrl = isOpenCmd && filteredArgs.length === 1;
|
|
177
|
+
if (isOpenNoUrl) filteredArgs.push("about:blank");
|
|
178
178
|
|
|
179
179
|
// bare `rech open` with no URL: warn if session already has tabs
|
|
180
180
|
if (isOpenCmd && filteredArgs.length === 1) {
|
|
@@ -186,19 +186,26 @@ export async function serve() {
|
|
|
186
186
|
stderr: "pipe",
|
|
187
187
|
env: { PATH: process.env.PATH, HOME: process.env.HOME },
|
|
188
188
|
});
|
|
189
|
-
const [listStatus, listOut] = await Promise.
|
|
190
|
-
listProc.exited,
|
|
191
|
-
new
|
|
189
|
+
const [listStatus, listOut] = await Promise.race([
|
|
190
|
+
Promise.all([listProc.exited, new Response(listProc.stdout).text()]),
|
|
191
|
+
new Promise<[number, string]>((resolve) =>
|
|
192
|
+
setTimeout(() => { listProc.kill(); resolve([1, ""]); }, 5000)
|
|
193
|
+
),
|
|
192
194
|
]);
|
|
193
195
|
if (listStatus === 0 && listOut.trim()) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
if (isOpenNoUrl) {
|
|
197
|
+
log(`session ${namespacedSession} already has tabs, returning tab-list hint`);
|
|
198
|
+
return Response.json({
|
|
199
|
+
status: 0,
|
|
200
|
+
stdout: listOut,
|
|
201
|
+
stderr: `[rech] session "${namespacedSession}" already has open tabs:\n`,
|
|
202
|
+
files: [],
|
|
203
|
+
existingSession: true,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// URL specified: navigate to it instead of returning tab-list
|
|
207
|
+
log(`session ${namespacedSession} already has tabs, converting open to goto`);
|
|
208
|
+
filteredArgs[0] = "goto";
|
|
202
209
|
}
|
|
203
210
|
} catch (e) {
|
|
204
211
|
log(`tab-list check failed: ${e}`);
|