
























@@ -1,8 +1,12 @@
1+import { execFileSync } from "node:child_process";
2+import fs from "node:fs";
13import path from "node:path";
24import { fileURLToPath } from "node:url";
3-import { defineConfig } from "vite";
5+import { defineConfig, type Plugin } from "vite";
4657const here = path.dirname(fileURLToPath(import.meta.url));
8+const repoRoot = path.resolve(here, "..");
9+const outDir = path.resolve(here, "../dist/control-ui");
610711function normalizeBase(input: string): string {
812const trimmed = input.trim();
@@ -18,17 +22,78 @@ function normalizeBase(input: string): string {
1822return `${trimmed}/`;
1923}
202425+function normalizeBuildId(input: string): string {
26+const normalized = input.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
27+return normalized.slice(0, 96) || "dev";
28+}
29+30+function readPackageVersion(): string {
31+try {
32+const raw = fs.readFileSync(path.join(repoRoot, "package.json"), "utf8");
33+const parsed = JSON.parse(raw) as { version?: unknown };
34+return typeof parsed.version === "string" && parsed.version.trim()
35+ ? parsed.version.trim()
36+ : "dev";
37+} catch {
38+return "dev";
39+}
40+}
41+42+function readGitShortSha(): string | null {
43+try {
44+const raw = execFileSync("git", ["-C", repoRoot, "rev-parse", "--short=12", "HEAD"], {
45+encoding: "utf8",
46+stdio: ["ignore", "pipe", "ignore"],
47+});
48+return raw.trim() || null;
49+} catch {
50+return null;
51+}
52+}
53+54+function resolveControlUiBuildId(): string {
55+const explicit =
56+process.env.OPENCLAW_CONTROL_UI_BUILD_ID?.trim() || process.env.OPENCLAW_VERSION?.trim();
57+if (explicit) {
58+return normalizeBuildId(explicit);
59+}
60+const version = readPackageVersion();
61+const gitSha = readGitShortSha();
62+return normalizeBuildId(gitSha ? `${version}-${gitSha}` : version);
63+}
64+65+function controlUiServiceWorkerBuildIdPlugin(buildId: string): Plugin {
66+return {
67+name: "control-ui-service-worker-build-id",
68+apply: "build",
69+closeBundle() {
70+const swPath = path.join(outDir, "sw.js");
71+const source = fs.readFileSync(swPath, "utf8");
72+const placeholder = '"__OPENCLAW_CONTROL_UI_BUILD_ID__"';
73+const updated = source.replace(placeholder, JSON.stringify(buildId));
74+if (updated === source) {
75+throw new Error(`Control UI service worker build id placeholder missing in ${swPath}`);
76+}
77+fs.writeFileSync(swPath, updated);
78+},
79+};
80+}
81+2182export default defineConfig(() => {
2283const envBase = process.env.OPENCLAW_CONTROL_UI_BASE_PATH?.trim();
2384const base = envBase ? normalizeBase(envBase) : "./";
85+const controlUiBuildId = resolveControlUiBuildId();
2486return {
2587 base,
88+define: {
89+__OPENCLAW_CONTROL_UI_BUILD_ID__: JSON.stringify(controlUiBuildId),
90+},
2691publicDir: path.resolve(here, "public"),
2792optimizeDeps: {
2893include: ["lit/directives/repeat.js"],
2994},
3095build: {
31-outDir: path.resolve(here, "../dist/control-ui"),
96+ outDir,
3297emptyOutDir: true,
3398sourcemap: true,
3499// Keep CI/onboard logs clean; current control UI chunking is intentionally above 500 kB.
@@ -40,6 +105,7 @@ export default defineConfig(() => {
40105strictPort: true,
41106},
42107plugins: [
108+controlUiServiceWorkerBuildIdPlugin(controlUiBuildId),
43109{
44110name: "control-ui-dev-stubs",
45111configureServer(server) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。