diff --git a/app/api/chat/[id]/route.ts b/app/api/chat/[id]/route.ts index 481fc8b..89a28bc 100644 --- a/app/api/chat/[id]/route.ts +++ b/app/api/chat/[id]/route.ts @@ -3,14 +3,8 @@ export const runtime = 'nodejs' import { getAgent } from '@/lib/agents' import { validateChatMessages } from '@/lib/validation' import { hasImageContent, extractImageAttachments, buildTextPrompt, sendViaOpenClaw } from '@/lib/anthropic' -import OpenAI from 'openai' -import { gatewayBaseUrl } from '@/lib/env' - -// Route through the OpenClaw gateway — no separate API key needed -const openai = new OpenAI({ - baseURL: gatewayBaseUrl(), - apiKey: process.env.OPENCLAW_GATEWAY_TOKEN, -}) +import { getOpenAIClient } from '@/lib/openai' +import type OpenAI from 'openai' const GATEWAY_TOKEN = process.env.OPENCLAW_GATEWAY_TOKEN || '' @@ -18,6 +12,7 @@ export async function POST( request: Request, { params }: { params: Promise<{ id: string }> } ) { + const openai = getOpenAIClient() const { id } = await params const agent = await getAgent(id) diff --git a/app/api/kanban/chat/[id]/route.ts b/app/api/kanban/chat/[id]/route.ts index f8336fe..f2fb890 100644 --- a/app/api/kanban/chat/[id]/route.ts +++ b/app/api/kanban/chat/[id]/route.ts @@ -1,13 +1,8 @@ export const runtime = 'nodejs' import { getAgent } from '@/lib/agents' -import OpenAI from 'openai' -import { gatewayBaseUrl } from '@/lib/env' - -const openai = new OpenAI({ - baseURL: gatewayBaseUrl(), - apiKey: process.env.OPENCLAW_GATEWAY_TOKEN, -}) +import { getOpenAIClient } from '@/lib/openai' +import type OpenAI from 'openai' const MAX_TITLE = 500 const MAX_DESC = 5000 @@ -27,6 +22,7 @@ export async function POST( request: Request, { params }: { params: Promise<{ id: string }> } ) { + const openai = getOpenAIClient() const { id } = await params const agent = await getAgent(id) diff --git a/app/api/transcribe/route.ts b/app/api/transcribe/route.ts index b2fba72..50958f1 100644 --- a/app/api/transcribe/route.ts +++ b/app/api/transcribe/route.ts @@ -1,14 +1,9 @@ export const runtime = 'nodejs' -import OpenAI from 'openai' -import { gatewayBaseUrl } from '@/lib/env' - -const openai = new OpenAI({ - baseURL: gatewayBaseUrl(), - apiKey: process.env.OPENCLAW_GATEWAY_TOKEN, -}) +import { getOpenAIClient } from '@/lib/openai' export async function POST(request: Request) { + const openai = getOpenAIClient() let formData: FormData try { formData = await request.formData() diff --git a/app/api/tts/route.ts b/app/api/tts/route.ts index ac36bcd..76a88d9 100644 --- a/app/api/tts/route.ts +++ b/app/api/tts/route.ts @@ -1,14 +1,9 @@ export const runtime = 'nodejs' -import OpenAI from 'openai' -import { gatewayBaseUrl } from '@/lib/env' - -const openai = new OpenAI({ - baseURL: gatewayBaseUrl(), - apiKey: process.env.OPENCLAW_GATEWAY_TOKEN, -}) +import { getOpenAIClient } from '@/lib/openai' export async function POST(request: Request) { + const openai = getOpenAIClient() try { const { text, voice } = await request.json() diff --git a/lib/openai.ts b/lib/openai.ts new file mode 100644 index 0000000..63c1576 --- /dev/null +++ b/lib/openai.ts @@ -0,0 +1,20 @@ +import OpenAI from 'openai' +import { gatewayBaseUrl } from './env' + +/** + * Lazy-initialized OpenAI client routed through the OpenClaw gateway. + * + * Call inside route handlers, not at module top level, so builds don't + * fail when environment variables aren't set. + */ +let _openai: OpenAI | null = null + +export function getOpenAIClient(): OpenAI { + if (!_openai) { + _openai = new OpenAI({ + baseURL: gatewayBaseUrl(), + apiKey: process.env.OPENCLAW_GATEWAY_TOKEN || '', + }) + } + return _openai +} diff --git a/lib/setup-detection.ts b/lib/setup-detection.ts index aba020c..b0c9cc7 100644 --- a/lib/setup-detection.ts +++ b/lib/setup-detection.ts @@ -21,15 +21,28 @@ import { execSync } from 'child_process' * Detect the default workspace path. * * Checks in order: - * 1. ~/.openclaw/agents/main/workspace (current agent-scoped layout) - * 2. ~/.openclaw/workspace-main (multi-agent layout, main agent) - * 3. ~/.openclaw/workspace-* (multi-agent layout, any agent) - * 4. ~/.openclaw/workspace (legacy single-workspace layout) + * 1. ~/.openclaw/openclaw.json agents.defaults.workspace (canonical) + * 2. ~/.openclaw/agents/main/workspace (current agent-scoped layout) + * 3. ~/.openclaw/workspace-main (multi-agent layout, main agent) + * 4. ~/.openclaw/workspace-* (multi-agent layout, any agent) + * 5. ~/.openclaw/workspace (legacy single-workspace layout) */ export function detectWorkspacePath(): string | null { const base = join(homedir(), '.openclaw') - // 1. Current agent-scoped layout + // 1. Read from openclaw.json (canonical source) + const configPath = join(base, 'openclaw.json') + if (existsSync(configPath)) { + try { + const config = JSON.parse(readFileSync(configPath, 'utf-8')) + const ws = config?.agents?.defaults?.workspace + if (typeof ws === 'string' && existsSync(ws)) return ws + } catch { + // Invalid JSON, fall through + } + } + + // 2. Current agent-scoped layout const agentPath = join(base, 'agents', 'main', 'workspace') if (existsSync(agentPath)) return agentPath diff --git a/package-lock.json b/package-lock.json index 30f870a..9144d8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clawport-ui", - "version": "0.8.7", + "version": "0.8.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "clawport-ui", - "version": "0.8.7", + "version": "0.8.8", "license": "MIT", "dependencies": { "@dagrejs/dagre": "^2.0.4", diff --git a/package.json b/package.json index b6d4ef0..3c41c72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clawport-ui", - "version": "0.8.7", + "version": "0.8.8", "description": "Open-source dashboard for managing, monitoring, and chatting with your OpenClaw AI agents.", "homepage": "https://clawport.dev", "repository": { diff --git a/scripts/setup.mjs b/scripts/setup.mjs index 6d7e5e4..6e4f91c 100644 --- a/scripts/setup.mjs +++ b/scripts/setup.mjs @@ -45,7 +45,19 @@ function exec(cmd) { function detectWorkspacePath() { const base = join(homedir(), '.openclaw') - // 1. Current agent-scoped layout + // 1. Read from openclaw.json (canonical source) + const configPath = join(base, 'openclaw.json') + if (existsSync(configPath)) { + try { + const config = JSON.parse(readFileSync(configPath, 'utf-8')) + const ws = config?.agents?.defaults?.workspace + if (typeof ws === 'string' && existsSync(ws)) return ws + } catch { + // Invalid JSON, fall through + } + } + + // 2. Current agent-scoped layout const agentPath = join(base, 'agents', 'main', 'workspace') if (existsSync(agentPath)) return agentPath