clawport-ui 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/api/chat/[id]/route.ts +3 -8
- package/app/api/kanban/chat/[id]/route.ts +3 -7
- package/app/api/transcribe/route.ts +2 -7
- package/app/api/tts/route.ts +2 -7
- package/lib/crons.test.ts +14 -7
- package/lib/crons.ts +19 -9
- package/lib/openai.ts +20 -0
- package/lib/setup-detection.ts +18 -5
- package/package.json +1 -1
- package/scripts/setup.mjs +13 -1
|
@@ -3,14 +3,8 @@ export const runtime = 'nodejs'
|
|
|
3
3
|
import { getAgent } from '@/lib/agents'
|
|
4
4
|
import { validateChatMessages } from '@/lib/validation'
|
|
5
5
|
import { hasImageContent, extractImageAttachments, buildTextPrompt, sendViaOpenClaw } from '@/lib/anthropic'
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
// Route through the OpenClaw gateway — no separate API key needed
|
|
10
|
-
const openai = new OpenAI({
|
|
11
|
-
baseURL: gatewayBaseUrl(),
|
|
12
|
-
apiKey: process.env.OPENCLAW_GATEWAY_TOKEN,
|
|
13
|
-
})
|
|
6
|
+
import { getOpenAIClient } from '@/lib/openai'
|
|
7
|
+
import type OpenAI from 'openai'
|
|
14
8
|
|
|
15
9
|
const GATEWAY_TOKEN = process.env.OPENCLAW_GATEWAY_TOKEN || ''
|
|
16
10
|
|
|
@@ -18,6 +12,7 @@ export async function POST(
|
|
|
18
12
|
request: Request,
|
|
19
13
|
{ params }: { params: Promise<{ id: string }> }
|
|
20
14
|
) {
|
|
15
|
+
const openai = getOpenAIClient()
|
|
21
16
|
const { id } = await params
|
|
22
17
|
const agent = await getAgent(id)
|
|
23
18
|
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
export const runtime = 'nodejs'
|
|
2
2
|
|
|
3
3
|
import { getAgent } from '@/lib/agents'
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
const openai = new OpenAI({
|
|
8
|
-
baseURL: gatewayBaseUrl(),
|
|
9
|
-
apiKey: process.env.OPENCLAW_GATEWAY_TOKEN,
|
|
10
|
-
})
|
|
4
|
+
import { getOpenAIClient } from '@/lib/openai'
|
|
5
|
+
import type OpenAI from 'openai'
|
|
11
6
|
|
|
12
7
|
const MAX_TITLE = 500
|
|
13
8
|
const MAX_DESC = 5000
|
|
@@ -27,6 +22,7 @@ export async function POST(
|
|
|
27
22
|
request: Request,
|
|
28
23
|
{ params }: { params: Promise<{ id: string }> }
|
|
29
24
|
) {
|
|
25
|
+
const openai = getOpenAIClient()
|
|
30
26
|
const { id } = await params
|
|
31
27
|
const agent = await getAgent(id)
|
|
32
28
|
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
export const runtime = 'nodejs'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { gatewayBaseUrl } from '@/lib/env'
|
|
5
|
-
|
|
6
|
-
const openai = new OpenAI({
|
|
7
|
-
baseURL: gatewayBaseUrl(),
|
|
8
|
-
apiKey: process.env.OPENCLAW_GATEWAY_TOKEN,
|
|
9
|
-
})
|
|
3
|
+
import { getOpenAIClient } from '@/lib/openai'
|
|
10
4
|
|
|
11
5
|
export async function POST(request: Request) {
|
|
6
|
+
const openai = getOpenAIClient()
|
|
12
7
|
let formData: FormData
|
|
13
8
|
try {
|
|
14
9
|
formData = await request.formData()
|
package/app/api/tts/route.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
export const runtime = 'nodejs'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { gatewayBaseUrl } from '@/lib/env'
|
|
5
|
-
|
|
6
|
-
const openai = new OpenAI({
|
|
7
|
-
baseURL: gatewayBaseUrl(),
|
|
8
|
-
apiKey: process.env.OPENCLAW_GATEWAY_TOKEN,
|
|
9
|
-
})
|
|
3
|
+
import { getOpenAIClient } from '@/lib/openai'
|
|
10
4
|
|
|
11
5
|
export async function POST(request: Request) {
|
|
6
|
+
const openai = getOpenAIClient()
|
|
12
7
|
try {
|
|
13
8
|
const { text, voice } = await request.json()
|
|
14
9
|
|
package/lib/crons.test.ts
CHANGED
|
@@ -254,18 +254,25 @@ describe('getCrons - error and lastError', () => {
|
|
|
254
254
|
})
|
|
255
255
|
})
|
|
256
256
|
|
|
257
|
-
// ---
|
|
257
|
+
// --- Graceful degradation (returns empty array on failure) ---
|
|
258
258
|
|
|
259
|
-
describe('getCrons -
|
|
260
|
-
it('
|
|
259
|
+
describe('getCrons - graceful degradation', () => {
|
|
260
|
+
it('returns empty array when execSync throws (CLI not installed)', async () => {
|
|
261
261
|
mockExecSync.mockImplementation(() => { throw new Error('ENOENT') })
|
|
262
|
-
await
|
|
263
|
-
|
|
262
|
+
const result = await getCrons()
|
|
263
|
+
expect(result).toEqual([])
|
|
264
264
|
})
|
|
265
265
|
|
|
266
|
-
it('
|
|
266
|
+
it('returns empty array for invalid JSON output', async () => {
|
|
267
267
|
mockExecSync.mockReturnValue('not valid json {{')
|
|
268
|
-
await
|
|
268
|
+
const result = await getCrons()
|
|
269
|
+
expect(result).toEqual([])
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('returns empty array when OPENCLAW_BIN is not set', async () => {
|
|
273
|
+
vi.unstubAllEnvs()
|
|
274
|
+
const result = await getCrons()
|
|
275
|
+
expect(result).toEqual([])
|
|
269
276
|
})
|
|
270
277
|
})
|
|
271
278
|
|
package/lib/crons.ts
CHANGED
|
@@ -32,11 +32,22 @@ export async function getCrons(): Promise<CronJob[]> {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
try {
|
|
35
|
-
const openclawBin =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
35
|
+
const openclawBin = process.env.OPENCLAW_BIN
|
|
36
|
+
if (!openclawBin) {
|
|
37
|
+
// No binary configured -- return empty list instead of crashing
|
|
38
|
+
return []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let raw: string
|
|
42
|
+
try {
|
|
43
|
+
raw = execSync(`${openclawBin} cron list --json`, {
|
|
44
|
+
encoding: 'utf-8',
|
|
45
|
+
timeout: 10000,
|
|
46
|
+
})
|
|
47
|
+
} catch {
|
|
48
|
+
// CLI failed (binary not found, no crons, gateway down) -- return empty
|
|
49
|
+
return []
|
|
50
|
+
}
|
|
40
51
|
|
|
41
52
|
const parsed = extractJson(raw) as Record<string, unknown>
|
|
42
53
|
const jobs: unknown[] = Array.isArray(parsed)
|
|
@@ -113,9 +124,8 @@ export async function getCrons(): Promise<CronJob[]> {
|
|
|
113
124
|
|
|
114
125
|
_cronsCache = { result, ts: Date.now() }
|
|
115
126
|
return result
|
|
116
|
-
} catch
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
127
|
+
} catch {
|
|
128
|
+
// JSON extraction or parsing failed -- return empty rather than 500
|
|
129
|
+
return []
|
|
120
130
|
}
|
|
121
131
|
}
|
package/lib/openai.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import OpenAI from 'openai'
|
|
2
|
+
import { gatewayBaseUrl } from './env'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lazy-initialized OpenAI client routed through the OpenClaw gateway.
|
|
6
|
+
*
|
|
7
|
+
* Call inside route handlers, not at module top level, so builds don't
|
|
8
|
+
* fail when environment variables aren't set.
|
|
9
|
+
*/
|
|
10
|
+
let _openai: OpenAI | null = null
|
|
11
|
+
|
|
12
|
+
export function getOpenAIClient(): OpenAI {
|
|
13
|
+
if (!_openai) {
|
|
14
|
+
_openai = new OpenAI({
|
|
15
|
+
baseURL: gatewayBaseUrl(),
|
|
16
|
+
apiKey: process.env.OPENCLAW_GATEWAY_TOKEN || '',
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
return _openai
|
|
20
|
+
}
|
package/lib/setup-detection.ts
CHANGED
|
@@ -21,15 +21,28 @@ import { execSync } from 'child_process'
|
|
|
21
21
|
* Detect the default workspace path.
|
|
22
22
|
*
|
|
23
23
|
* Checks in order:
|
|
24
|
-
* 1. ~/.openclaw/agents
|
|
25
|
-
* 2. ~/.openclaw/workspace
|
|
26
|
-
* 3. ~/.openclaw/workspace
|
|
27
|
-
* 4. ~/.openclaw/workspace
|
|
24
|
+
* 1. ~/.openclaw/openclaw.json agents.defaults.workspace (canonical)
|
|
25
|
+
* 2. ~/.openclaw/agents/main/workspace (current agent-scoped layout)
|
|
26
|
+
* 3. ~/.openclaw/workspace-main (multi-agent layout, main agent)
|
|
27
|
+
* 4. ~/.openclaw/workspace-* (multi-agent layout, any agent)
|
|
28
|
+
* 5. ~/.openclaw/workspace (legacy single-workspace layout)
|
|
28
29
|
*/
|
|
29
30
|
export function detectWorkspacePath(): string | null {
|
|
30
31
|
const base = join(homedir(), '.openclaw')
|
|
31
32
|
|
|
32
|
-
// 1.
|
|
33
|
+
// 1. Read from openclaw.json (canonical source)
|
|
34
|
+
const configPath = join(base, 'openclaw.json')
|
|
35
|
+
if (existsSync(configPath)) {
|
|
36
|
+
try {
|
|
37
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
38
|
+
const ws = config?.agents?.defaults?.workspace
|
|
39
|
+
if (typeof ws === 'string' && existsSync(ws)) return ws
|
|
40
|
+
} catch {
|
|
41
|
+
// Invalid JSON, fall through
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. Current agent-scoped layout
|
|
33
46
|
const agentPath = join(base, 'agents', 'main', 'workspace')
|
|
34
47
|
if (existsSync(agentPath)) return agentPath
|
|
35
48
|
|
package/package.json
CHANGED
package/scripts/setup.mjs
CHANGED
|
@@ -45,7 +45,19 @@ function exec(cmd) {
|
|
|
45
45
|
function detectWorkspacePath() {
|
|
46
46
|
const base = join(homedir(), '.openclaw')
|
|
47
47
|
|
|
48
|
-
// 1.
|
|
48
|
+
// 1. Read from openclaw.json (canonical source)
|
|
49
|
+
const configPath = join(base, 'openclaw.json')
|
|
50
|
+
if (existsSync(configPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
53
|
+
const ws = config?.agents?.defaults?.workspace
|
|
54
|
+
if (typeof ws === 'string' && existsSync(ws)) return ws
|
|
55
|
+
} catch {
|
|
56
|
+
// Invalid JSON, fall through
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2. Current agent-scoped layout
|
|
49
61
|
const agentPath = join(base, 'agents', 'main', 'workspace')
|
|
50
62
|
if (existsSync(agentPath)) return agentPath
|
|
51
63
|
|