Skip to content

Commit fb3dd29

Browse files
committed
feat(app): embed web ui in binary with flags to use proxy
1 parent 759ec10 commit fb3dd29

3 files changed

Lines changed: 53 additions & 13 deletions

File tree

packages/opencode/script/build.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ console.log(`Loaded ${migrations.length} migrations`)
5454
const singleFlag = process.argv.includes("--single")
5555
const baselineFlag = process.argv.includes("--baseline")
5656
const skipInstall = process.argv.includes("--skip-install")
57+
const skipEmbedWebUi = process.argv.includes("--skip-embed-web-ui")
58+
59+
60+
const createEmbeddedWebUIBundle = async()=>{
61+
console.log(`Building Web UI to embed in the binary`);
62+
const appDir = path.join(import.meta.dirname, "../../app")
63+
await $`bun run --cwd ${appDir} build`;
64+
const allFiles = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: path.join(appDir, "dist")}));
65+
const fileMap = `
66+
// Import all files as file_$i with type: "file"
67+
${allFiles.map((filePath, i) => `import file_${i} from "${path.join(appDir, "dist", filePath)}" with { type: "file" };`).join("\n")}
68+
// Export with original mappings
69+
export default {
70+
${allFiles.map((filePath, i)=>`"${filePath}": file_${i},`).join("\n")}
71+
}
72+
`.trim()
73+
return fileMap;
74+
}
75+
76+
const embeddedFileMap = skipEmbedWebUi ? null : await createEmbeddedWebUIBundle();
5777

5878
const allTargets: {
5979
os: string
@@ -179,7 +199,10 @@ for (const item of targets) {
179199
execArgv: [`--user-agent=opencode/${Script.version}`, "--use-system-ca", "--"],
180200
windows: {},
181201
},
182-
entrypoints: ["./src/index.ts", parserWorker, workerPath],
202+
files: {
203+
...(embeddedFileMap ? { "opencode-web-ui.gen.ts": embeddedFileMap } : {}),
204+
},
205+
entrypoints: ["./src/index.ts", parserWorker, workerPath, ...(embeddedFileMap ? ["opencode-web-ui.gen.ts"] : [])],
183206
define: {
184207
OPENCODE_VERSION: `'${Script.version}'`,
185208
OPENCODE_MIGRATIONS: JSON.stringify(migrations),

packages/opencode/src/flag/flag.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export namespace Flag {
5353
export const OPENCODE_EXPERIMENTAL_MARKDOWN = truthy("OPENCODE_EXPERIMENTAL_MARKDOWN")
5454
export const OPENCODE_MODELS_URL = process.env["OPENCODE_MODELS_URL"]
5555
export const OPENCODE_MODELS_PATH = process.env["OPENCODE_MODELS_PATH"]
56+
export const OPENCODE_DISABLE_EMBEDDED_WEB_UI = truthy("OPENCODE_DISABLE_EMBEDDED_WEB_UI")
5657

5758
function number(key: string) {
5859
const value = process.env[key]

packages/opencode/src/server/server.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -539,20 +539,36 @@ export namespace Server {
539539
},
540540
)
541541
.all("/*", async (c) => {
542+
const embeddedWebUI = Flag.OPENCODE_DISABLE_EMBEDDED_WEB_UI
543+
? null
544+
// @ts-expect-error - generated file at build time, esm modules imports are cached already
545+
: await import("opencode-web-ui.gen.ts").then((module) => module.default as Record<string, string>).catch(() => null);
542546
const path = c.req.path
543547

544-
const response = await proxy(`https://app.opencode.ai${path}`, {
545-
...c.req,
546-
headers: {
547-
...c.req.raw.headers,
548-
host: "app.opencode.ai",
549-
},
550-
})
551-
response.headers.set(
552-
"Content-Security-Policy",
553-
"default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:",
554-
)
555-
return response
548+
if (embeddedWebUI) {
549+
const match = embeddedWebUI[path.replace(/^\//, "")] ?? embeddedWebUI["index.html"] ?? null;
550+
if (!match) return c.json({ error: "Not Found" }, 404)
551+
const file = Bun.file(match)
552+
if (await file.exists()) {
553+
c.header("Content-Type", file.type)
554+
return c.body(await file.arrayBuffer())
555+
} else {
556+
return c.json({ error: "Not Found" }, 404)
557+
}
558+
} else {
559+
const response = await proxy(`https://app.opencode.ai${path}`, {
560+
...c.req,
561+
headers: {
562+
...c.req.raw.headers,
563+
host: "app.opencode.ai",
564+
},
565+
})
566+
response.headers.set(
567+
"Content-Security-Policy",
568+
"default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:",
569+
)
570+
return response
571+
}
556572
}) as unknown as Hono,
557573
)
558574

0 commit comments

Comments
 (0)