refactor(browser): split browser cores via build ARG, add core selector (#237)

* refactor(browser): split browser cores via build ARG, add core selector

- Replace playwright official image with ubuntu:noble base in both
  docker/Dockerfile.browser and devenv/Dockerfile.browser; install
  browsers at build time driven by ARG/ENV BROWSER_CORES
- Add GET /cores endpoint to Browser Gateway reporting available cores
- Proxy GET /browser-contexts/cores in Go handler to Browser Gateway
- Add `core` field to BrowserContextConfigModel and GatewayBrowserContext;
  context creation selects the appropriate browser instance by core
- Frontend context-setting page fetches available cores and renders a
  core selector; saves core as part of the config JSON
- install.sh prompts for browser core selection and writes BROWSER_CORES
  to .env; builds the browser image locally before docker compose up
- Regenerate OpenAPI spec and TypeScript SDK

* fix: lint
This commit is contained in:
Acbox Liu
2026-03-14 12:37:20 +08:00
committed by GitHub
parent 627b673a5c
commit c8728ffc2c
22 changed files with 364 additions and 33 deletions
+35 -6
View File
@@ -1,7 +1,36 @@
import { chromium } from 'playwright' import { chromium, firefox } from 'playwright'
import type { Browser } from 'playwright'
export const initBrowser = async () => { export type BrowserCore = 'chromium' | 'firefox'
return await chromium.launch({
headless: true, export const browsers = new Map<BrowserCore, Browser>()
})
} export const initBrowsers = async (): Promise<Map<BrowserCore, Browser>> => {
const raw = process.env.BROWSER_CORES ?? 'chromium'
const cores = raw.split(',').map(s => s.trim()) as BrowserCore[]
for (const core of cores) {
if (core === 'chromium') {
browsers.set('chromium', await chromium.launch({ headless: true }))
} else if (core === 'firefox') {
browsers.set('firefox', await firefox.launch({ headless: true }))
}
}
if (browsers.size === 0) {
browsers.set('chromium', await chromium.launch({ headless: true }))
}
return browsers
}
export const getBrowser = (core: BrowserCore = 'chromium'): Browser => {
const b = browsers.get(core) ?? browsers.values().next().value
if (!b) throw new Error(`Browser core "${core}" is not available`)
return b
}
export const getAvailableCores = (): BrowserCore[] => {
const raw = process.env.BROWSER_CORES ?? 'chromium'
return raw.split(',').map(s => s.trim()) as BrowserCore[]
}
+9 -4
View File
@@ -2,13 +2,16 @@ import { Elysia } from 'elysia'
import { loadConfig } from '@memoh/config' import { loadConfig } from '@memoh/config'
import { corsMiddleware } from './middlewares/cors' import { corsMiddleware } from './middlewares/cors'
import { errorMiddleware } from './middlewares/error' import { errorMiddleware } from './middlewares/error'
import { initBrowser } from './browser' import { initBrowsers, browsers } from './browser'
import { contextModule } from './modules/context' import { contextModule } from './modules/context'
import { devicesModule } from './modules/devices' import { devicesModule } from './modules/devices'
import { coresModule } from './modules/cores'
const config = loadConfig('../../config.toml') const config = loadConfig('../../config.toml')
export const browser = await initBrowser() await initBrowsers()
export { browsers }
const app = new Elysia() const app = new Elysia()
.use(corsMiddleware) .use(corsMiddleware)
@@ -16,10 +19,13 @@ const app = new Elysia()
.get('/health', () => ({ .get('/health', () => ({
status: 'ok', status: 'ok',
})) }))
.use(coresModule)
.use(contextModule) .use(contextModule)
.use(devicesModule) .use(devicesModule)
.onStop(async () => { .onStop(async () => {
await browser.close() for (const browser of browsers.values()) {
await browser.close()
}
}) })
.listen({ .listen({
port: config.browser_gateway.port ?? 8083, port: config.browser_gateway.port ?? 8083,
@@ -28,4 +34,3 @@ const app = new Elysia()
}) })
console.log(`🌐 Browser Gateway is running at ${app.server!.url}`) console.log(`🌐 Browser Gateway is running at ${app.server!.url}`)
+1
View File
@@ -1,6 +1,7 @@
import { z } from 'zod' import { z } from 'zod'
export const BrowserContextConfigModel = z.object({ export const BrowserContextConfigModel = z.object({
core: z.enum(['chromium', 'firefox']).optional().default('chromium'),
viewport: z.object({ viewport: z.object({
width: z.number(), width: z.number(),
height: z.number(), height: z.number(),
+6 -4
View File
@@ -2,7 +2,7 @@ import { Elysia } from 'elysia'
import { storage } from '../storage' import { storage } from '../storage'
import { z } from 'zod' import { z } from 'zod'
import { BrowserContextConfigModel } from '../models' import { BrowserContextConfigModel } from '../models'
import { browser } from '..' import { getBrowser } from '../browser'
import { actionModule } from './action' import { actionModule } from './action'
export const contextModule = new Elysia({ prefix: '/context' }) export const contextModule = new Elysia({ prefix: '/context' })
@@ -14,7 +14,7 @@ export const contextModule = new Elysia({ prefix: '/context' })
const { id } = query const { id } = query
const entry = storage.get(id) const entry = storage.get(id)
if (!entry) return null if (!entry) return null
return { id: entry.id, name: entry.name, config: entry.config } return { id: entry.id, name: entry.name, core: entry.core, config: entry.config }
}, { }, {
query: z.object({ query: z.object({
id: z.string(), id: z.string(),
@@ -24,6 +24,8 @@ export const contextModule = new Elysia({ prefix: '/context' })
'/', '/',
async ({ body }) => { async ({ body }) => {
const { name, config, id } = body const { name, config, id } = body
const core = config.core ?? 'chromium'
const browser = getBrowser(core)
const context = await browser.newContext({ const context = await browser.newContext({
viewport: config.viewport, viewport: config.viewport,
userAgent: config.userAgent, userAgent: config.userAgent,
@@ -37,8 +39,8 @@ export const contextModule = new Elysia({ prefix: '/context' })
ignoreHTTPSErrors: config.ignoreHTTPSErrors, ignoreHTTPSErrors: config.ignoreHTTPSErrors,
proxy: config.proxy, proxy: config.proxy,
}) })
storage.set(id, { id, name, context, config }) storage.set(id, { id, name, core, context, config })
return { id, name, config } return { id, name, core, config }
}, },
{ {
body: z.object({ body: z.object({
+7
View File
@@ -0,0 +1,7 @@
import { Elysia } from 'elysia'
import { getAvailableCores } from '../browser'
export const coresModule = new Elysia({ prefix: '/cores' })
.get('/', () => {
return { cores: getAvailableCores() }
})
+2
View File
@@ -1,9 +1,11 @@
import type { BrowserContext, Page } from 'playwright' import type { BrowserContext, Page } from 'playwright'
import type { BrowserContextConfig } from '../models' import type { BrowserContextConfig } from '../models'
import type { BrowserCore } from '../browser'
export interface GatewayBrowserContext { export interface GatewayBrowserContext {
id: string id: string
name: string name: string
core: BrowserCore
context: BrowserContext context: BrowserContext
config: BrowserContextConfig config: BrowserContextConfig
activePage?: Page activePage?: Page
+3
View File
@@ -393,6 +393,9 @@
"timezoneId": "Timezone", "timezoneId": "Timezone",
"timezonePlaceholder": "e.g. America/New_York", "timezonePlaceholder": "e.g. America/New_York",
"ignoreHTTPSErrors": "Ignore HTTPS Errors", "ignoreHTTPSErrors": "Ignore HTTPS Errors",
"core": "Browser Core",
"chromium": "Chromium",
"firefox": "Firefox",
"config": "Configuration" "config": "Configuration"
}, },
"mcp": { "mcp": {
+3
View File
@@ -389,6 +389,9 @@
"timezoneId": "时区", "timezoneId": "时区",
"timezonePlaceholder": "例如 Asia/Shanghai", "timezonePlaceholder": "例如 Asia/Shanghai",
"ignoreHTTPSErrors": "忽略 HTTPS 错误", "ignoreHTTPSErrors": "忽略 HTTPS 错误",
"core": "浏览器内核",
"chromium": "Chromium",
"firefox": "Firefox",
"config": "配置" "config": "配置"
}, },
"mcp": { "mcp": {
@@ -41,6 +41,31 @@
{{ $t('browserContext.config') }} {{ $t('browserContext.config') }}
</h3> </h3>
<FormField
v-slot="{ value, handleChange }"
name="core"
>
<FormItem>
<Label>{{ $t('browserContext.core') }}</Label>
<FormControl>
<div class="flex gap-3">
<button
v-for="c in availableCores"
:key="c"
type="button"
class="flex items-center gap-2 px-3 py-1.5 rounded-md border text-sm transition-colors"
:class="value === c
? 'border-primary bg-primary/10 text-primary font-medium'
: 'border-border bg-card text-muted-foreground hover:bg-accent'"
@click="handleChange(c)"
>
{{ $t(`browserContext.${c}`) }}
</button>
</div>
</FormControl>
</FormItem>
</FormField>
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<FormField <FormField
v-slot="{ componentField }" v-slot="{ componentField }"
@@ -212,10 +237,11 @@ import {
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import z from 'zod' import z from 'zod'
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import { useMutation, useQueryCache } from '@pinia/colada' import { useMutation, useQuery, useQueryCache } from '@pinia/colada'
import { putBrowserContextsById, deleteBrowserContextsById } from '@memoh/sdk' import { putBrowserContextsById, deleteBrowserContextsById } from '@memoh/sdk'
import { getBrowserContextsCoresQuery } from '@memoh/sdk/colada'
import type { BrowsercontextsBrowserContext } from '@memoh/sdk' import type { BrowsercontextsBrowserContext } from '@memoh/sdk'
import { inject, watch, type Ref } from 'vue' import { inject, watch, computed, type Ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { toast } from 'vue-sonner' import { toast } from 'vue-sonner'
import { useDialogMutation } from '@/composables/useDialogMutation' import { useDialogMutation } from '@/composables/useDialogMutation'
@@ -228,7 +254,11 @@ const queryCache = useQueryCache()
const curContext = inject<Ref<BrowsercontextsBrowserContext | undefined>>('curBrowserContext') const curContext = inject<Ref<BrowsercontextsBrowserContext | undefined>>('curBrowserContext')
const { data: coresData } = useQuery(getBrowserContextsCoresQuery())
const availableCores = computed(() => coresData.value?.cores ?? ['chromium'])
interface ConfigShape { interface ConfigShape {
core?: string
viewport?: { width?: number; height?: number } viewport?: { width?: number; height?: number }
userAgent?: string userAgent?: string
deviceScaleFactor?: number deviceScaleFactor?: number
@@ -251,6 +281,7 @@ function parseConfig(ctx: BrowsercontextsBrowserContext | undefined): ConfigShap
const schema = toTypedSchema(z.object({ const schema = toTypedSchema(z.object({
name: z.string().min(1), name: z.string().min(1),
core: z.enum(['chromium', 'firefox']).optional(),
viewportWidth: z.coerce.number().optional(), viewportWidth: z.coerce.number().optional(),
viewportHeight: z.coerce.number().optional(), viewportHeight: z.coerce.number().optional(),
userAgent: z.string().optional(), userAgent: z.string().optional(),
@@ -269,6 +300,7 @@ watch(() => curContext?.value, (ctx) => {
form.resetForm({ form.resetForm({
values: { values: {
name: ctx.name || '', name: ctx.name || '',
core: (cfg.core as 'chromium' | 'firefox') ?? 'chromium',
viewportWidth: cfg.viewport?.width ?? 1280, viewportWidth: cfg.viewport?.width ?? 1280,
viewportHeight: cfg.viewport?.height ?? 720, viewportHeight: cfg.viewport?.height ?? 720,
userAgent: cfg.userAgent ?? '', userAgent: cfg.userAgent ?? '',
@@ -307,7 +339,9 @@ const handleSave = form.handleSubmit(async (values) => {
const id = curContext?.value?.id const id = curContext?.value?.id
if (!id) return if (!id) return
const config: Record<string, any> = {} const config: Record<string, any> = {
core: values.core ?? 'chromium',
}
if (values.viewportWidth || values.viewportHeight) { if (values.viewportWidth || values.viewportHeight) {
config.viewport = { config.viewport = {
width: values.viewportWidth || 1280, width: values.viewportWidth || 1280,
+13 -3
View File
@@ -1,7 +1,17 @@
FROM mcr.microsoft.com/playwright:v1.50.0-noble FROM ubuntu:noble
ARG BROWSER_CORES=chromium,firefox
ENV BROWSER_CORES=$BROWSER_CORES
RUN apt-get update && apt-get install -y --no-install-recommends unzip curl nodejs npm && \
curl -fsSL https://bun.sh/install | bash && \
npm install -g playwright@1.50.0 && \
for core in $(echo "$BROWSER_CORES" | tr ',' ' '); do \
npx playwright install --with-deps "$core"; \
done && \
npm uninstall -g playwright && \
apt-get clean && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:${PATH}" ENV PATH="/root/.bun/bin:${PATH}"
WORKDIR /workspace WORKDIR /workspace
+4
View File
@@ -141,9 +141,13 @@ services:
build: build:
context: .. context: ..
dockerfile: devenv/Dockerfile.browser dockerfile: devenv/Dockerfile.browser
args:
BROWSER_CORES: ${BROWSER_CORES:-chromium,firefox}
container_name: memoh-dev-browser container_name: memoh-dev-browser
working_dir: /workspace/apps/browser working_dir: /workspace/apps/browser
command: ["bun", "run", "--watch", "src/index.ts"] command: ["bun", "run", "--watch", "src/index.ts"]
environment:
- BROWSER_CORES=${BROWSER_CORES:-chromium,firefox}
volumes: volumes:
- ..:/workspace - ..:/workspace
- node_modules:/workspace/node_modules - node_modules:/workspace/node_modules
+7
View File
@@ -118,8 +118,15 @@ services:
browser: browser:
image: memohai/browser:latest image: memohai/browser:latest
build:
context: .
dockerfile: docker/Dockerfile.browser
args:
BROWSER_CORES: ${BROWSER_CORES:-chromium,firefox}
container_name: memoh-browser container_name: memoh-browser
profiles: [browser] profiles: [browser]
environment:
- BROWSER_CORES=${BROWSER_CORES:-chromium,firefox}
volumes: volumes:
- ${MEMOH_CONFIG:-./config.toml}:/config.toml:ro - ${MEMOH_CONFIG:-./config.toml}:/config.toml:ro
ports: ports:
+7 -2
View File
@@ -15,7 +15,10 @@ COPY apps/browser/ ./apps/browser/
RUN cd apps/browser && bun run build RUN cd apps/browser && bun run build
FROM mcr.microsoft.com/playwright:v1.50.0-noble FROM ubuntu:noble
ARG BROWSER_CORES=chromium,firefox
ENV BROWSER_CORES=$BROWSER_CORES
WORKDIR /app WORKDIR /app
@@ -29,7 +32,9 @@ COPY --from=builder /build/apps/browser/node_modules /app/node_modules
COPY --from=builder /build/apps/browser/package.json /app/package.json COPY --from=builder /build/apps/browser/package.json /app/package.json
COPY --from=builder /build/node_modules /node_modules COPY --from=builder /build/node_modules /node_modules
RUN npx playwright install --with-deps chromium RUN for core in $(echo "$BROWSER_CORES" | tr ',' ' '); do \
bun /app/node_modules/.bin/playwright install --with-deps "$core"; \
done
EXPOSE 8083 EXPOSE 8083
+50 -5
View File
@@ -1,6 +1,9 @@
package handlers package handlers
import ( import (
"encoding/json"
"fmt"
"io"
"log/slog" "log/slog"
"net/http" "net/http"
"strings" "strings"
@@ -8,17 +11,20 @@ import (
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/memohai/memoh/internal/browsercontexts" "github.com/memohai/memoh/internal/browsercontexts"
"github.com/memohai/memoh/internal/config"
) )
type BrowserContextsHandler struct { type BrowserContextsHandler struct {
service *browsercontexts.Service service *browsercontexts.Service
logger *slog.Logger logger *slog.Logger
browserGatewayURL string
} }
func NewBrowserContextsHandler(log *slog.Logger, service *browsercontexts.Service) *BrowserContextsHandler { func NewBrowserContextsHandler(log *slog.Logger, service *browsercontexts.Service, cfg config.Config) *BrowserContextsHandler {
return &BrowserContextsHandler{ return &BrowserContextsHandler{
service: service, service: service,
logger: log.With(slog.String("handler", "browser_contexts")), logger: log.With(slog.String("handler", "browser_contexts")),
browserGatewayURL: cfg.BrowserGateway.BaseURL(),
} }
} }
@@ -26,6 +32,7 @@ func (h *BrowserContextsHandler) Register(e *echo.Echo) {
group := e.Group("/browser-contexts") group := e.Group("/browser-contexts")
group.POST("", h.Create) group.POST("", h.Create)
group.GET("", h.List) group.GET("", h.List)
group.GET("/cores", h.GetCores)
group.GET("/:id", h.Get) group.GET("/:id", h.Get)
group.PUT("/:id", h.Update) group.PUT("/:id", h.Update)
group.DELETE("/:id", h.Delete) group.DELETE("/:id", h.Delete)
@@ -139,3 +146,41 @@ func (h *BrowserContextsHandler) Delete(c echo.Context) error {
} }
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)
} }
// GetCores godoc
// @Summary Get available browser cores
// @Description Get the list of browser cores available in the Browser Gateway container
// @Tags browser-contexts
// @Produce json
// @Success 200 {object} BrowserCoresResponse
// @Failure 502 {object} ErrorResponse
// @Router /browser-contexts/cores [get].
func (h *BrowserContextsHandler) GetCores(c echo.Context) error {
url := fmt.Sprintf("%s/cores/", h.browserGatewayURL)
req, err := http.NewRequestWithContext(c.Request().Context(), http.MethodGet, url, nil)
if err != nil {
return echo.NewHTTPError(http.StatusBadGateway, "failed to create request")
}
resp, err := http.DefaultClient.Do(req) //nolint:gosec // URL is from trusted internal config
if err != nil {
h.logger.Warn("browser gateway unreachable", slog.String("error", err.Error()))
return c.JSON(http.StatusOK, BrowserCoresResponse{Cores: []string{"chromium"}})
}
defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body)
if err != nil {
return echo.NewHTTPError(http.StatusBadGateway, "failed to read browser gateway response")
}
var result BrowserCoresResponse
if err := json.Unmarshal(body, &result); err != nil {
return echo.NewHTTPError(http.StatusBadGateway, "failed to parse browser gateway response")
}
return c.JSON(http.StatusOK, result)
}
// BrowserCoresResponse is the response for the GetCores endpoint.
type BrowserCoresResponse struct {
Cores []string `json:"cores"`
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+29
View File
@@ -611,6 +611,10 @@ export type HandlersBatchDeleteRequest = {
ids?: Array<string>; ids?: Array<string>;
}; };
export type HandlersBrowserCoresResponse = {
cores?: Array<string>;
};
export type HandlersChannelMeta = { export type HandlersChannelMeta = {
capabilities?: ChannelChannelCapabilities; capabilities?: ChannelChannelCapabilities;
config_schema?: ChannelConfigSchema; config_schema?: ChannelConfigSchema;
@@ -5944,6 +5948,31 @@ export type PostBrowserContextsResponses = {
export type PostBrowserContextsResponse = PostBrowserContextsResponses[keyof PostBrowserContextsResponses]; export type PostBrowserContextsResponse = PostBrowserContextsResponses[keyof PostBrowserContextsResponses];
export type GetBrowserContextsCoresData = {
body?: never;
path?: never;
query?: never;
url: '/browser-contexts/cores';
};
export type GetBrowserContextsCoresErrors = {
/**
* Bad Gateway
*/
502: HandlersErrorResponse;
};
export type GetBrowserContextsCoresError = GetBrowserContextsCoresErrors[keyof GetBrowserContextsCoresErrors];
export type GetBrowserContextsCoresResponses = {
/**
* OK
*/
200: HandlersBrowserCoresResponse;
};
export type GetBrowserContextsCoresResponse = GetBrowserContextsCoresResponses[keyof GetBrowserContextsCoresResponses];
export type DeleteBrowserContextsByIdData = { export type DeleteBrowserContextsByIdData = {
body?: never; body?: never;
path: { path: {
+21 -1
View File
@@ -99,6 +99,7 @@ WORKSPACE="$WORKSPACE_DEFAULT"
MEMOH_DATA_DIR="$MEMOH_DATA_DIR_DEFAULT" MEMOH_DATA_DIR="$MEMOH_DATA_DIR_DEFAULT"
USE_CN_MIRROR="${USE_CN_MIRROR:-false}" USE_CN_MIRROR="${USE_CN_MIRROR:-false}"
USE_SPARSE="${USE_SPARSE:-false}" USE_SPARSE="${USE_SPARSE:-false}"
BROWSER_CORES="${BROWSER_CORES:-chromium,firefox}"
if [ "$SILENT" = false ]; then if [ "$SILENT" = false ]; then
echo "Configure Memoh (press Enter to use defaults):" > /dev/tty echo "Configure Memoh (press Enter to use defaults):" > /dev/tty
@@ -148,6 +149,19 @@ if [ "$SILENT" = false ]; then
y|Y|yes|YES) USE_SPARSE=true ;; y|Y|yes|YES) USE_SPARSE=true ;;
esac esac
echo "" > /dev/tty
echo " Browser core selection:" > /dev/tty
echo " 1) Chromium only (smaller image)" > /dev/tty
echo " 2) Firefox only" > /dev/tty
echo " 3) Both Chromium and Firefox (default)" > /dev/tty
printf " Browser core [3]: " > /dev/tty
read -r input < /dev/tty || true
case "$input" in
1) BROWSER_CORES="chromium" ;;
2) BROWSER_CORES="firefox" ;;
*) BROWSER_CORES="chromium,firefox" ;;
esac
echo "" > /dev/tty echo "" > /dev/tty
fi fi
@@ -222,11 +236,17 @@ fi
echo POSTGRES_PASSWORD="${PG_PASS}" >> .env echo POSTGRES_PASSWORD="${PG_PASS}" >> .env
echo MEMOH_CONFIG=./config.toml >> .env echo MEMOH_CONFIG=./config.toml >> .env
echo MEMOH_DATA_DIR="{$MEMOH_DATA_DIR}" >> .env echo MEMOH_DATA_DIR="{$MEMOH_DATA_DIR}" >> .env
echo BROWSER_CORES="${BROWSER_CORES}" >> .env
echo USE_SPARSE="${USE_SPARSE}" >> .env echo USE_SPARSE="${USE_SPARSE}" >> .env
echo "${GREEN}✓ Browser cores: ${BROWSER_CORES}${NC}"
echo "" echo ""
echo "${GREEN}Pulling latest Docker images...${NC}" echo "${GREEN}Pulling latest Docker images...${NC}"
$DOCKER compose $COMPOSE_FILES $COMPOSE_PROFILES pull $DOCKER compose $COMPOSE_FILES $COMPOSE_PROFILES pull --ignore-buildable
echo ""
echo "${GREEN}Building browser image (cores: ${BROWSER_CORES})...${NC}"
$DOCKER compose $COMPOSE_FILES $COMPOSE_PROFILES build browser
echo "" echo ""
echo "${GREEN}Starting services (first startup may take a few minutes)...${NC}" echo "${GREEN}Starting services (first startup may take a few minutes)...${NC}"
+37
View File
@@ -5482,6 +5482,32 @@ const docTemplate = `{
} }
} }
}, },
"/browser-contexts/cores": {
"get": {
"description": "Get the list of browser cores available in the Browser Gateway container",
"produces": [
"application/json"
],
"tags": [
"browser-contexts"
],
"summary": "Get available browser cores",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BrowserCoresResponse"
}
},
"502": {
"description": "Bad Gateway",
"schema": {
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
}
},
"/browser-contexts/{id}": { "/browser-contexts/{id}": {
"get": { "get": {
"description": "Get browser context by ID", "description": "Get browser context by ID",
@@ -10039,6 +10065,17 @@ const docTemplate = `{
} }
} }
}, },
"handlers.BrowserCoresResponse": {
"type": "object",
"properties": {
"cores": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"handlers.ChannelMeta": { "handlers.ChannelMeta": {
"type": "object", "type": "object",
"properties": { "properties": {
+37
View File
@@ -5473,6 +5473,32 @@
} }
} }
}, },
"/browser-contexts/cores": {
"get": {
"description": "Get the list of browser cores available in the Browser Gateway container",
"produces": [
"application/json"
],
"tags": [
"browser-contexts"
],
"summary": "Get available browser cores",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BrowserCoresResponse"
}
},
"502": {
"description": "Bad Gateway",
"schema": {
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
}
},
"/browser-contexts/{id}": { "/browser-contexts/{id}": {
"get": { "get": {
"description": "Get browser context by ID", "description": "Get browser context by ID",
@@ -10030,6 +10056,17 @@
} }
} }
}, },
"handlers.BrowserCoresResponse": {
"type": "object",
"properties": {
"cores": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"handlers.ChannelMeta": { "handlers.ChannelMeta": {
"type": "object", "type": "object",
"properties": { "properties": {
+25
View File
@@ -1001,6 +1001,13 @@ definitions:
type: string type: string
type: array type: array
type: object type: object
handlers.BrowserCoresResponse:
properties:
cores:
items:
type: string
type: array
type: object
handlers.ChannelMeta: handlers.ChannelMeta:
properties: properties:
capabilities: capabilities:
@@ -6178,6 +6185,24 @@ paths:
summary: Update a browser context summary: Update a browser context
tags: tags:
- browser-contexts - browser-contexts
/browser-contexts/cores:
get:
description: Get the list of browser cores available in the Browser Gateway
container
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.BrowserCoresResponse'
"502":
description: Bad Gateway
schema:
$ref: '#/definitions/handlers.ErrorResponse'
summary: Get available browser cores
tags:
- browser-contexts
/channels: /channels:
get: get:
description: List channel meta information including capabilities and schemas description: List channel meta information including capabilities and schemas