feat: tfcode

This commit is contained in:
Gab
2026-03-27 17:09:02 +11:00
parent 95340af79a
commit cbcaa4ba46
22 changed files with 84 additions and 62 deletions

View File

@@ -5,6 +5,9 @@ import { withNetworkOptions, resolveNetworkOptions } from "../network"
import { Flag } from "../../flag/flag"
import open from "open"
import { networkInterfaces } from "os"
import { existsSync } from "fs"
import path from "path"
import { $ } from "bun"
function getNetworkIPs() {
const nets = networkInterfaces()
@@ -15,12 +18,8 @@ function getNetworkIPs() {
if (!net) continue
for (const netInfo of net) {
// Skip internal and non-IPv4 addresses
if (netInfo.internal || netInfo.family !== "IPv4") continue
// Skip Docker bridge networks (typically 172.x.x.x)
if (netInfo.address.startsWith("172.")) continue
results.push(netInfo.address)
}
}
@@ -28,6 +27,18 @@ function getNetworkIPs() {
return results
}
async function ensureAppBuilt() {
const appDir = path.join(import.meta.dirname, "..", "..", "..", "..", "app")
const distDir = path.join(appDir, "dist")
if (!existsSync(distDir) || !existsSync(path.join(distDir, "index.html"))) {
UI.println(UI.Style.TEXT_INFO_BOLD + " Building web app...", UI.Style.TEXT_NORMAL)
const result = $`cd ${appDir} && bun run build`.quiet()
await result
UI.println(UI.Style.TEXT_SUCCESS_BOLD + " Build complete!", UI.Style.TEXT_NORMAL)
}
}
export const WebCommand = cmd({
command: "web",
builder: (yargs) => withNetworkOptions(yargs),
@@ -36,6 +47,9 @@ export const WebCommand = cmd({
if (!Flag.OPENCODE_SERVER_PASSWORD) {
UI.println(UI.Style.TEXT_WARNING_BOLD + "! " + "OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
}
await ensureAppBuilt()
const opts = await resolveNetworkOptions(args)
const server = Server.listen(opts)
UI.empty()
@@ -43,11 +57,9 @@ export const WebCommand = cmd({
UI.empty()
if (opts.hostname === "0.0.0.0") {
// Show localhost for local access
const localhostUrl = `http://localhost:${server.port}`
UI.println(UI.Style.TEXT_INFO_BOLD + " Local access: ", UI.Style.TEXT_NORMAL, localhostUrl)
// Show network IPs for remote access
const networkIPs = getNetworkIPs()
if (networkIPs.length > 0) {
for (const ip of networkIPs) {
@@ -67,7 +79,6 @@ export const WebCommand = cmd({
)
}
// Open localhost in browser
open(localhostUrl.toString()).catch(() => {})
} else {
const displayUrl = server.url.toString()

View File

@@ -2,9 +2,9 @@ import { Log } from "../util/log"
import { describeRoute, generateSpecs, validator, resolver, openAPIRouteHandler } from "hono-openapi"
import { Hono } from "hono"
import { cors } from "hono/cors"
import { proxy } from "hono/proxy"
import { basicAuth } from "hono/basic-auth"
import z from "zod"
import path from "path"
import { Provider } from "../provider/provider"
import { NamedError } from "@opencode-ai/util/error"
import { LSP } from "../lsp"
@@ -222,9 +222,9 @@ export namespace Server {
openAPIRouteHandler(app, {
documentation: {
info: {
title: "opencode",
title: "tfcode",
version: "0.0.3",
description: "opencode api",
description: "tfcode api",
},
openapi: "3.1.1",
},
@@ -529,20 +529,33 @@ export namespace Server {
},
)
.all("/*", async (c) => {
const path = c.req.path
const reqPath = c.req.path === "/" ? "/index.html" : c.req.path
const appDist = path.join(import.meta.dirname, "..", "..", "..", "app", "dist")
const publicDir = path.join(import.meta.dirname, "..", "..", "..", "app", "public")
const response = await proxy(`https://app.opencode.ai${path}`, {
...c.req,
headers: {
...c.req.raw.headers,
host: "app.opencode.ai",
},
})
response.headers.set(
"Content-Security-Policy",
"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:",
)
return response
let filePath = path.join(appDist, reqPath)
let file = Bun.file(filePath)
if (!(await file.exists())) {
filePath = path.join(publicDir, reqPath)
file = Bun.file(filePath)
}
if (!(await file.exists())) {
filePath = path.join(appDist, "index.html")
file = Bun.file(filePath)
}
if (await file.exists()) {
const response = new Response(file)
const contentType = file.type
if (contentType) {
response.headers.set("Content-Type", contentType)
}
return response
}
return c.text("Not Found. Run 'bun run build' in packages/app first.", 404)
})
}
@@ -551,9 +564,9 @@ export namespace Server {
const result = await generateSpecs(Default(), {
documentation: {
info: {
title: "opencode",
title: "tfcode",
version: "1.0.0",
description: "opencode api",
description: "tfcode api",
},
openapi: "3.1.1",
},