From cbcaa4ba46b06705de6ce98671e75bab64ef0888 Mon Sep 17 00:00:00 2001 From: Gab Date: Fri, 27 Mar 2026 17:09:02 +1100 Subject: [PATCH] feat: tfcode --- bun.lock | 2 +- packages/app/src/i18n/ar.ts | 2 +- packages/app/src/i18n/br.ts | 2 +- packages/app/src/i18n/bs.ts | 2 +- packages/app/src/i18n/da.ts | 2 +- packages/app/src/i18n/de.ts | 2 +- packages/app/src/i18n/en.ts | 6 +-- packages/app/src/i18n/es.ts | 2 +- packages/app/src/i18n/fr.ts | 2 +- packages/app/src/i18n/ja.ts | 2 +- packages/app/src/i18n/ko.ts | 2 +- packages/app/src/i18n/no.ts | 2 +- packages/app/src/i18n/pl.ts | 2 +- packages/app/src/i18n/ru.ts | 2 +- packages/app/src/i18n/th.ts | 2 +- packages/app/src/i18n/zh.ts | 2 +- packages/app/src/i18n/zht.ts | 2 +- packages/desktop-electron/src/main/index.ts | 8 ++-- packages/tfcode/src/cli/cmd/web.ts | 25 ++++++++--- packages/tfcode/src/server/server.ts | 49 +++++++++++++-------- packages/ui/src/components/logo.tsx | 24 +++++----- packages/ui/src/theme/themes/opencode.json | 2 +- 22 files changed, 84 insertions(+), 62 deletions(-) diff --git a/bun.lock b/bun.lock index 808e9f64c..db75d918a 100644 --- a/bun.lock +++ b/bun.lock @@ -381,7 +381,7 @@ }, "packages/tfcode": { "name": "tfcode", - "version": "1.0.14", + "version": "1.0.16", "bin": { "tfcode": "./bin/tfcode", }, diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index c8f58c796..001db0f52 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -542,7 +542,7 @@ export const dict = { "sidebar.project.recentSessions": "الجلسات الحديثة", "sidebar.project.viewAllSessions": "عرض جميع الجلسات", "sidebar.project.clearNotifications": "مسح الإشعارات", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "سطح المكتب", "settings.section.server": "الخادم", "settings.tab.general": "عام", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 3112e91bb..7a5b9cb1d 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -549,7 +549,7 @@ export const dict = { "sidebar.project.recentSessions": "Sessões recentes", "sidebar.project.viewAllSessions": "Ver todas as sessões", "sidebar.project.clearNotifications": "Limpar notificações", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Servidor", "settings.tab.general": "Geral", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index f2dbd8493..e0f0ccb8c 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -611,7 +611,7 @@ export const dict = { "sidebar.project.viewAllSessions": "Prikaži sve sesije", "sidebar.project.clearNotifications": "Očisti obavijesti", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index e90e1071a..08cced577 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -607,7 +607,7 @@ export const dict = { "sidebar.project.viewAllSessions": "Vis alle sessioner", "sidebar.project.clearNotifications": "Ryd notifikationer", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", "settings.tab.general": "Generelt", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 69658b29e..2b936d2a6 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -558,7 +558,7 @@ export const dict = { "sidebar.project.recentSessions": "Letzte Sitzungen", "sidebar.project.viewAllSessions": "Alle Sitzungen anzeigen", "sidebar.project.clearNotifications": "Benachrichtigungen löschen", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", "settings.tab.general": "Allgemein", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index fb1064ab6..758b99694 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -119,7 +119,7 @@ export const dict = { "dialog.model.manage.description": "Customize which models appear in the model selector.", "dialog.model.manage.provider.toggle": "Toggle all {{provider}} models", - "dialog.model.unpaid.freeModels.title": "Free models provided by OpenCode", + "dialog.model.unpaid.freeModels.title": "Free models provided by TF Code", "dialog.model.unpaid.addMore.title": "Add more models from popular providers", "dialog.provider.viewAll": "Show more providers", @@ -571,7 +571,7 @@ export const dict = { "session.revertDock.expand": "Expand rolled back messages", "session.revertDock.restore": "Restore message", - "session.new.title": "Build anything", + "session.new.title": "Build with TF Code", "session.new.worktree.main": "Main branch", "session.new.worktree.mainWithBranch": "Main branch ({{branch}})", "session.new.worktree.create": "Create new worktree", @@ -710,7 +710,7 @@ export const dict = { "debugBar.mem.tipUnavailable": "Used JS heap vs heap limit. Chromium only.", "debugBar.mem.tip": "Used JS heap vs heap limit. {{used}} of {{limit}}.", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 9e36e4de6..4a50a4dbf 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -614,7 +614,7 @@ export const dict = { "sidebar.project.viewAllSessions": "Ver todas las sesiones", "sidebar.project.clearNotifications": "Borrar notificaciones", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Escritorio", "settings.section.server": "Servidor", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index f53b3882c..0795bc41a 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -556,7 +556,7 @@ export const dict = { "sidebar.project.recentSessions": "Sessions récentes", "sidebar.project.viewAllSessions": "Voir toutes les sessions", "sidebar.project.clearNotifications": "Effacer les notifications", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Bureau", "settings.section.server": "Serveur", "settings.tab.general": "Général", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index d66a7341d..616b9bf30 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -546,7 +546,7 @@ export const dict = { "sidebar.project.recentSessions": "最近のセッション", "sidebar.project.viewAllSessions": "すべてのセッションを表示", "sidebar.project.clearNotifications": "通知をクリア", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "デスクトップ", "settings.section.server": "サーバー", "settings.tab.general": "一般", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index d534c27e8..1faf27e20 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -547,7 +547,7 @@ export const dict = { "sidebar.project.recentSessions": "최근 세션", "sidebar.project.viewAllSessions": "모든 세션 보기", "sidebar.project.clearNotifications": "알림 지우기", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "데스크톱", "settings.section.server": "서버", "settings.tab.general": "일반", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index c23d0a279..5ac66e378 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -614,7 +614,7 @@ export const dict = { "sidebar.project.viewAllSessions": "Vis alle sesjoner", "sidebar.project.clearNotifications": "Fjern varsler", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Skrivebord", "settings.section.server": "Server", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index dac847b21..b650244c8 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -547,7 +547,7 @@ export const dict = { "sidebar.project.recentSessions": "Ostatnie sesje", "sidebar.project.viewAllSessions": "Zobacz wszystkie sesje", "sidebar.project.clearNotifications": "Wyczyść powiadomienia", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Pulpit", "settings.section.server": "Serwer", "settings.tab.general": "Ogólne", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 684d5deec..9a6c86cae 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -612,7 +612,7 @@ export const dict = { "sidebar.project.viewAllSessions": "Посмотреть все сессии", "sidebar.project.clearNotifications": "Очистить уведомления", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "Приложение", "settings.section.server": "Сервер", "settings.tab.general": "Основные", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 80f0da94e..fc897830f 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -605,7 +605,7 @@ export const dict = { "sidebar.project.viewAllSessions": "ดูเซสชันทั้งหมด", "sidebar.project.clearNotifications": "ล้างการแจ้งเตือน", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "เดสก์ท็อป", "settings.section.server": "เซิร์ฟเวอร์", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index cf64ca9b2..88ed4de71 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -604,7 +604,7 @@ export const dict = { "sidebar.project.viewAllSessions": "查看全部会话", "sidebar.project.clearNotifications": "清除通知", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "桌面", "settings.section.server": "服务器", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 02c00d17a..92505c0a6 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -601,7 +601,7 @@ export const dict = { "sidebar.project.viewAllSessions": "查看全部工作階段", "sidebar.project.clearNotifications": "清除通知", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "TF Code Desktop", "settings.section.desktop": "桌面", "settings.section.server": "伺服器", "settings.tab.general": "一般", diff --git a/packages/desktop-electron/src/main/index.ts b/packages/desktop-electron/src/main/index.ts index 032343204..f54acdc98 100644 --- a/packages/desktop-electron/src/main/index.ts +++ b/packages/desktop-electron/src/main/index.ts @@ -9,16 +9,16 @@ import { app, BrowserWindow, dialog } from "electron" import pkg from "electron-updater" const APP_NAMES: Record = { - dev: "OpenCode Dev", - beta: "OpenCode Beta", - prod: "OpenCode", + dev: "TF Code Dev", + beta: "TF Code Beta", + prod: "TF Code", } const APP_IDS: Record = { dev: "ai.opencode.desktop.dev", beta: "ai.opencode.desktop.beta", prod: "ai.opencode.desktop", } -app.setName(app.isPackaged ? APP_NAMES[CHANNEL] : "OpenCode Dev") +app.setName(app.isPackaged ? APP_NAMES[CHANNEL] : "TF Code Dev") app.setPath("userData", join(app.getPath("appData"), app.isPackaged ? APP_IDS[CHANNEL] : "ai.opencode.desktop.dev")) const { autoUpdater } = pkg diff --git a/packages/tfcode/src/cli/cmd/web.ts b/packages/tfcode/src/cli/cmd/web.ts index fbe8343ef..68cf569cb 100644 --- a/packages/tfcode/src/cli/cmd/web.ts +++ b/packages/tfcode/src/cli/cmd/web.ts @@ -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() diff --git a/packages/tfcode/src/server/server.ts b/packages/tfcode/src/server/server.ts index bcb025da0..8cd43dbca 100644 --- a/packages/tfcode/src/server/server.ts +++ b/packages/tfcode/src/server/server.ts @@ -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", }, diff --git a/packages/ui/src/components/logo.tsx b/packages/ui/src/components/logo.tsx index 20c2f3fbe..14b437a5e 100644 --- a/packages/ui/src/components/logo.tsx +++ b/packages/ui/src/components/logo.tsx @@ -35,7 +35,7 @@ export const Logo = (props: { class?: string }) => { return ( @@ -44,18 +44,16 @@ export const Logo = (props: { class?: string }) => { - - - - - - - - - - - - + + Code + ) diff --git a/packages/ui/src/theme/themes/opencode.json b/packages/ui/src/theme/themes/opencode.json index de7ab19e5..0baa92ab0 100644 --- a/packages/ui/src/theme/themes/opencode.json +++ b/packages/ui/src/theme/themes/opencode.json @@ -1,6 +1,6 @@ { "$schema": "https://opencode.ai/desktop-theme.json", - "name": "OpenCode", + "name": "TF Code", "id": "opencode", "light": { "palette": {