mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-01 06:42:26 +00:00
feat: tfcode
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user