feat(webterm): 配置浏览器性能策略

This commit is contained in:
Codex
2026-07-11 13:11:39 +02:00
parent 32232badaf
commit 37e1be9497
2 changed files with 55 additions and 0 deletions
+41
View File
@@ -25,6 +25,7 @@ interface WebtermConfig {
autoResumePrompt: AutoResumePrompt;
browserTerminalHistory: BrowserTerminalHistory;
browserOutputPolicy: BrowserOutputPolicy;
browserPerformance: BrowserPerformance;
targets: WebtermTarget[];
}
@@ -52,6 +53,15 @@ interface BrowserOutputPolicy {
maxFrameBytes: number;
}
interface BrowserPerformance {
animationsEnabled: boolean;
defaultBackgroundMode: "disconnect" | "receive-only" | "live";
backgroundModes: Array<{ id: "disconnect" | "receive-only" | "live"; label: string }>;
deferredOutputMaxBytes: number;
dashboardSampleWindowSeconds: number;
dashboardRefreshIntervalMs: number;
}
interface WebtermTarget {
id: string;
enabled: boolean;
@@ -74,6 +84,7 @@ interface WebtermTarget {
autoResumePrompt: AutoResumePrompt;
browserTerminalHistory: BrowserTerminalHistory;
browserOutputPolicy: BrowserOutputPolicy;
browserPerformance: BrowserPerformance;
environment: Record<string, string>;
};
publicExposure: {
@@ -301,6 +312,7 @@ function readWebtermConfig(): WebtermConfig {
const autoResumePrompt = parseAutoResumePrompt(recordField(yaml, "autoResumePrompt", "webterm"), "webterm.autoResumePrompt");
const browserTerminalHistory = parseBrowserTerminalHistory(recordField(yaml, "browserTerminalHistory", "webterm"), "webterm.browserTerminalHistory");
const browserOutputPolicy = parseBrowserOutputPolicy(recordField(yaml, "browserOutputPolicy", "webterm"), "webterm.browserOutputPolicy");
const browserPerformance = parseBrowserPerformance(recordField(yaml, "browserPerformance", "webterm"), "webterm.browserPerformance");
const targetsRaw = yaml.targets;
if (!Array.isArray(targetsRaw)) throw new Error("webterm.targets must be an array");
return {
@@ -309,6 +321,7 @@ function readWebtermConfig(): WebtermConfig {
autoResumePrompt,
browserTerminalHistory,
browserOutputPolicy,
browserPerformance,
targets: targetsRaw.map((item, index) => parseTarget(
asRecord(item, `webterm.targets[${index}]`),
`webterm.targets[${index}]`,
@@ -316,6 +329,7 @@ function readWebtermConfig(): WebtermConfig {
autoResumePrompt,
browserTerminalHistory,
browserOutputPolicy,
browserPerformance,
)),
};
}
@@ -327,6 +341,7 @@ function parseTarget(
autoResumePrompt: AutoResumePrompt,
browserTerminalHistory: BrowserTerminalHistory,
browserOutputPolicy: BrowserOutputPolicy,
browserPerformance: BrowserPerformance,
): WebtermTarget {
const runtime = recordField(record, "runtime", path);
const exposure = recordField(record, "publicExposure", path);
@@ -358,6 +373,7 @@ function parseTarget(
autoResumePrompt,
browserTerminalHistory,
browserOutputPolicy,
browserPerformance,
environment,
},
publicExposure: {
@@ -404,6 +420,7 @@ function renderCompose(target: WebtermTarget): string {
AUTO_RESUME_PROMPT_JSON: JSON.stringify(target.runtime.autoResumePrompt),
BROWSER_TERMINAL_HISTORY_JSON: JSON.stringify(target.runtime.browserTerminalHistory),
BROWSER_OUTPUT_POLICY_JSON: JSON.stringify(target.runtime.browserOutputPolicy),
BROWSER_PERFORMANCE_JSON: JSON.stringify(target.runtime.browserPerformance),
};
const envLines = Object.entries(environment)
.map(([key, value]) => ` ${key}: ${quoteYaml(value)}`)
@@ -509,6 +526,7 @@ function targetSummary(target: WebtermTarget): Record<string, unknown> {
},
browserTerminalHistory: target.runtime.browserTerminalHistory,
browserOutputPolicy: target.runtime.browserOutputPolicy,
browserPerformance: target.runtime.browserPerformance,
sourceMounts: target.runtime.sourceMounts.map((mount) => `${mount.source}->${mount.target}${mount.readOnly ? ":ro" : ""}`),
hostPort: target.runtime.hostPort,
containerPort: target.runtime.containerPort,
@@ -710,6 +728,29 @@ function parseBrowserOutputPolicy(record: Record<string, unknown>, path: string)
};
}
function parseBrowserPerformance(record: Record<string, unknown>, path: string): BrowserPerformance {
const allowedModes = ["disconnect", "receive-only", "live"] as const;
const defaultBackgroundMode = stringField(record, "defaultBackgroundMode", path);
if (!allowedModes.includes(defaultBackgroundMode as typeof allowedModes[number])) {
throw new Error(`${path}.defaultBackgroundMode must be disconnect, receive-only or live`);
}
const modesRecord = recordField(record, "backgroundModes", path);
const backgroundModes = allowedModes.map((id) => ({
id,
label: stringField(recordField(modesRecord, id, `${path}.backgroundModes`), "label", `${path}.backgroundModes.${id}`),
}));
const unknownModes = Object.keys(modesRecord).filter((id) => !allowedModes.includes(id as typeof allowedModes[number]));
if (unknownModes.length > 0) throw new Error(`${path}.backgroundModes contains unsupported modes: ${unknownModes.join(", ")}`);
return {
animationsEnabled: booleanField(record, "animationsEnabled", path),
defaultBackgroundMode: defaultBackgroundMode as BrowserPerformance["defaultBackgroundMode"],
backgroundModes,
deferredOutputMaxBytes: positiveIntegerField(record, "deferredOutputMaxBytes", path),
dashboardSampleWindowSeconds: positiveIntegerField(record, "dashboardSampleWindowSeconds", path),
dashboardRefreshIntervalMs: positiveIntegerField(record, "dashboardRefreshIntervalMs", path),
};
}
function positiveIntegerField(record: Record<string, unknown>, key: string, path: string): number {
const value = integerField(record, key, path);
if (value <= 0) throw new Error(`${path}.${key} must be positive`);