feat(browser): add remote-capable profiles
Co-authored-by: James Groat <james@groat.com>
This commit is contained in:
92
src/browser/profiles.ts
Normal file
92
src/browser/profiles.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* CDP port allocation for browser profiles.
|
||||
*
|
||||
* Port range: 18800-18899 (100 profiles max)
|
||||
* Ports are allocated once at profile creation and persisted in config.
|
||||
*
|
||||
* Reserved ports (do not use for CDP):
|
||||
* 18789 - Gateway WebSocket
|
||||
* 18790 - Bridge
|
||||
* 18791 - Browser control server
|
||||
* 18792-18799 - Reserved for future one-off services (canvas at 18793)
|
||||
*/
|
||||
|
||||
export const CDP_PORT_RANGE_START = 18800;
|
||||
export const CDP_PORT_RANGE_END = 18899;
|
||||
|
||||
export const PROFILE_NAME_REGEX = /^[a-z0-9][a-z0-9-]*$/;
|
||||
|
||||
export function isValidProfileName(name: string): boolean {
|
||||
if (!name || name.length > 64) return false;
|
||||
return PROFILE_NAME_REGEX.test(name);
|
||||
}
|
||||
|
||||
export function allocateCdpPort(usedPorts: Set<number>): number | null {
|
||||
for (let port = CDP_PORT_RANGE_START; port <= CDP_PORT_RANGE_END; port++) {
|
||||
if (!usedPorts.has(port)) return port;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getUsedPorts(
|
||||
profiles: Record<string, { cdpPort?: number; cdpUrl?: string }> | undefined,
|
||||
): Set<number> {
|
||||
if (!profiles) return new Set();
|
||||
const used = new Set<number>();
|
||||
for (const profile of Object.values(profiles)) {
|
||||
if (typeof profile.cdpPort === "number") {
|
||||
used.add(profile.cdpPort);
|
||||
continue;
|
||||
}
|
||||
const rawUrl = profile.cdpUrl?.trim();
|
||||
if (!rawUrl) continue;
|
||||
try {
|
||||
const parsed = new URL(rawUrl);
|
||||
const port =
|
||||
parsed.port && Number.parseInt(parsed.port, 10) > 0
|
||||
? Number.parseInt(parsed.port, 10)
|
||||
: parsed.protocol === "https:"
|
||||
? 443
|
||||
: 80;
|
||||
if (!Number.isNaN(port) && port > 0 && port <= 65535) {
|
||||
used.add(port);
|
||||
}
|
||||
} catch {
|
||||
// ignore invalid URLs
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
export const PROFILE_COLORS = [
|
||||
"#FF4500", // Orange-red (clawd default)
|
||||
"#0066CC", // Blue
|
||||
"#00AA00", // Green
|
||||
"#9933FF", // Purple
|
||||
"#FF6699", // Pink
|
||||
"#00CCCC", // Cyan
|
||||
"#FF9900", // Orange
|
||||
"#6666FF", // Indigo
|
||||
"#CC3366", // Magenta
|
||||
"#339966", // Teal
|
||||
];
|
||||
|
||||
export function allocateColor(usedColors: Set<string>): string {
|
||||
// Find first unused color from palette
|
||||
for (const color of PROFILE_COLORS) {
|
||||
if (!usedColors.has(color.toUpperCase())) {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
// All colors used, cycle based on count
|
||||
const index = usedColors.size % PROFILE_COLORS.length;
|
||||
// biome-ignore lint/style/noNonNullAssertion: Array is non-empty constant
|
||||
return PROFILE_COLORS[index] ?? PROFILE_COLORS[0]!;
|
||||
}
|
||||
|
||||
export function getUsedColors(
|
||||
profiles: Record<string, { color: string }> | undefined,
|
||||
): Set<string> {
|
||||
if (!profiles) return new Set();
|
||||
return new Set(Object.values(profiles).map((p) => p.color.toUpperCase()));
|
||||
}
|
||||
Reference in New Issue
Block a user