mirror of
https://github.com/JohnRiceML/clawport-ui.git
synced 2026-08-14 00:47:50 +00:00
fix: onboarding misreports cron parse failure as gateway error (#23)
When OpenClaw CLI prints debug log lines (e.g. "[plugins] [debug] ...") to stdout before JSON, extractJson() failed because bracketed log lines look like JSON array starts. Now tries each candidate position until one actually parses. Also fixes the onboarding wizard to show a specific "CLI log output" error instead of the misleading "Could not reach gateway" message when /api/crons fails due to JSON parsing. Bumps to v0.8.6. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6b61daa6f6
commit
7c44a6d969
@@ -164,15 +164,24 @@ export function OnboardingWizard({ forceOpen, onClose }: OnboardingWizardProps)
|
||||
|
||||
// Check crons (validates gateway + openclaw binary)
|
||||
fetch('/api/crons')
|
||||
.then(r => {
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`)
|
||||
.then(async r => {
|
||||
if (!r.ok) {
|
||||
const body = await r.json().catch(() => null)
|
||||
const serverMsg = body?.error
|
||||
throw new Error(serverMsg || `HTTP ${r.status}`)
|
||||
}
|
||||
return r.json()
|
||||
})
|
||||
.then(() => {
|
||||
setCronsStatus('ok')
|
||||
})
|
||||
.catch(() => {
|
||||
setCronsError('Could not reach OpenClaw gateway. Run: openclaw gateway run')
|
||||
.catch((err: Error) => {
|
||||
const msg = err.message || ''
|
||||
if (msg.includes('Failed to fetch cron') || msg.includes('JSON') || msg.includes('Unexpected token')) {
|
||||
setCronsError('Cron list failed -- OpenClaw CLI may be printing log output before JSON. Try: OPENCLAW_LOG_LEVEL=error clawport dev')
|
||||
} else {
|
||||
setCronsError('Could not reach OpenClaw gateway. Run: openclaw gateway run')
|
||||
}
|
||||
setCronsStatus('error')
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,4 +41,20 @@ hooks: Unrecognized key: "allowedAgentIds"
|
||||
it('throws on empty string', () => {
|
||||
expect(() => extractJson('')).toThrow()
|
||||
})
|
||||
|
||||
it('skips bracketed log lines like [plugins] before real JSON', () => {
|
||||
const raw = `[plugins] [debug] Database schema initialized
|
||||
[plugins] [debug] Loading context engine
|
||||
{"jobs":[{"name":"daily-report","schedule":"0 8 * * *"}]}`
|
||||
const result = extractJson(raw) as Record<string, unknown>
|
||||
expect(result.jobs).toEqual([{ name: 'daily-report', schedule: '0 8 * * *' }])
|
||||
})
|
||||
|
||||
it('skips bracketed log lines before JSON array', () => {
|
||||
const raw = `[plugins] [debug] Database schema initialized
|
||||
[plugins] [info] Ready
|
||||
[{"id":"pulse","name":"daily-pulse"}]`
|
||||
const result = extractJson(raw)
|
||||
expect(result).toEqual([{ id: 'pulse', name: 'daily-pulse' }])
|
||||
})
|
||||
})
|
||||
|
||||
+25
-12
@@ -1,25 +1,38 @@
|
||||
/**
|
||||
* Extract a JSON value from CLI output that may contain non-JSON preamble.
|
||||
*
|
||||
* Some OpenClaw versions print validation warnings (e.g. "Unrecognized key")
|
||||
* to stdout before the JSON payload. This function finds the first `[` or `{`
|
||||
* and parses from there, so ClawPort doesn't break on noisy CLI output.
|
||||
* Some OpenClaw versions print validation warnings or debug log lines
|
||||
* (e.g. "[plugins] [debug] ...") to stdout before the JSON payload.
|
||||
* This function finds the actual JSON structure by trying each `[` or `{`
|
||||
* position until one parses successfully.
|
||||
*/
|
||||
export function extractJson(raw: string): unknown {
|
||||
// Fast path: raw is already valid JSON
|
||||
const trimmed = raw.trim()
|
||||
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
||||
return JSON.parse(trimmed)
|
||||
try {
|
||||
return JSON.parse(trimmed)
|
||||
} catch {
|
||||
// May start with [ but be a log line like "[plugins] ..." -- fall through
|
||||
}
|
||||
}
|
||||
|
||||
// Find the first JSON structure in the output
|
||||
const arrStart = raw.indexOf('[')
|
||||
const objStart = raw.indexOf('{')
|
||||
const starts = [arrStart, objStart].filter(i => i >= 0)
|
||||
if (starts.length === 0) {
|
||||
throw new SyntaxError('No JSON found in CLI output')
|
||||
// Try each potential JSON start position
|
||||
let pos = 0
|
||||
while (pos < raw.length) {
|
||||
const arrStart = raw.indexOf('[', pos)
|
||||
const objStart = raw.indexOf('{', pos)
|
||||
const candidates = [arrStart, objStart].filter(i => i >= 0)
|
||||
if (candidates.length === 0) break
|
||||
|
||||
const start = Math.min(...candidates)
|
||||
try {
|
||||
return JSON.parse(raw.slice(start))
|
||||
} catch {
|
||||
// This wasn't the real JSON start -- advance past it
|
||||
pos = start + 1
|
||||
}
|
||||
}
|
||||
|
||||
const start = Math.min(...starts)
|
||||
return JSON.parse(raw.slice(start))
|
||||
throw new SyntaxError('No JSON found in CLI output')
|
||||
}
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "clawport-ui",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "clawport-ui",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dagrejs/dagre": "^2.0.4",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawport-ui",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"description": "Open-source dashboard for managing, monitoring, and chatting with your OpenClaw AI agents.",
|
||||
"homepage": "https://clawport.dev",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user