improve(control-ui): compress and cache bundled assets (#105890)

* perf(control-ui): compress bundled assets

* chore(control-ui): remove release-owned changelog edit

* fix(control-ui): release assets before compression

* refactor(control-ui): split static response helpers

* perf(control-ui): precompress bundled assets

* refactor(control-ui): keep encoding internals private

* chore(control-ui): refresh LOC baseline
This commit is contained in:
Peter Steinberger
2026-07-12 23:13:04 -07:00
committed by GitHub
parent 70b3467484
commit 16fa19cd72
10 changed files with 842 additions and 132 deletions

View File

@@ -0,0 +1,43 @@
#!/usr/bin/env node
// Verifies each generated Control UI sidecar encodes the final emitted asset bytes.
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { brotliDecompressSync, gunzipSync } from "node:zlib";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const assetsDir = path.join(repoRoot, "dist", "control-ui", "assets");
const errors = [];
let checked = 0;
for (const entry of fs.readdirSync(assetsDir, { withFileTypes: true })) {
if (!entry.isFile()) {
continue;
}
const suffix = entry.name.endsWith(".br") ? ".br" : entry.name.endsWith(".gz") ? ".gz" : null;
if (!suffix) {
continue;
}
const sidecarPath = path.join(assetsDir, entry.name);
const sourcePath = sidecarPath.slice(0, -suffix.length);
try {
const encoded = fs.readFileSync(sidecarPath);
const decoded = suffix === ".br" ? brotliDecompressSync(encoded) : gunzipSync(encoded);
const source = fs.readFileSync(sourcePath);
if (!decoded.equals(source)) {
errors.push(`${entry.name}: decoded bytes differ from ${path.basename(sourcePath)}`);
}
} catch (error) {
errors.push(`${entry.name}: ${error instanceof Error ? error.message : String(error)}`);
}
checked += 1;
}
if (checked === 0) {
errors.push("no precompressed Control UI assets found");
}
if (errors.length > 0) {
throw new Error(`Control UI precompressed asset verification failed:\n${errors.join("\n")}`);
}
process.stdout.write(`verified ${checked} finalized Control UI sidecars\n`);

View File

@@ -14,6 +14,7 @@ const requiredPreparedPathGroups = [
["dist/control-ui/index.html"],
];
const requiredControlUiAssetPrefix = "dist/control-ui/assets/";
const requiredControlUiCompressionSuffixes = [".br", ".gz"] as const;
const DEFAULT_PREPACK_COMMAND_TIMEOUT_MS = 30 * 60 * 1000;
const ALLOW_UNRELEASED_CHANGELOG_ENV = "OPENCLAW_PREPACK_ALLOW_UNRELEASED_CHANGELOG";
@@ -42,6 +43,13 @@ export function collectPreparedPrepackErrors(
}
if (!normalizedAssets.values().next().done) {
for (const suffix of requiredControlUiCompressionSuffixes) {
if (!Array.from(normalizedAssets).some((assetPath) => assetPath.endsWith(suffix))) {
errors.push(
`missing prepared Control UI ${suffix} asset under ${requiredControlUiAssetPrefix}`,
);
}
}
return errors;
}

View File

@@ -878,7 +878,7 @@
"src/gateway/cli-session-history.claude.ts": 602,
"src/gateway/config-reload.ts": 1022,
"src/gateway/control-ui-session-prs.ts": 648,
"src/gateway/control-ui.ts": 1189,
"src/gateway/control-ui.ts": 1152,
"src/gateway/exec-approval-manager.ts": 1141,
"src/gateway/gateway-cli-backend.live-helpers.ts": 648,
"src/gateway/gateway-cli-backend.live-probe-helpers.ts": 513,