clawport-ui 0.8.5 → 0.8.6
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/components/OnboardingWizard.tsx +13 -4
- package/lib/cli-utils.test.ts +16 -0
- package/lib/cli-utils.ts +25 -12
- package/package.json +1 -1
|
@@ -164,15 +164,24 @@ export function OnboardingWizard({ forceOpen, onClose }: OnboardingWizardProps)
|
|
|
164
164
|
|
|
165
165
|
// Check crons (validates gateway + openclaw binary)
|
|
166
166
|
fetch('/api/crons')
|
|
167
|
-
.then(r => {
|
|
168
|
-
if (!r.ok)
|
|
167
|
+
.then(async r => {
|
|
168
|
+
if (!r.ok) {
|
|
169
|
+
const body = await r.json().catch(() => null)
|
|
170
|
+
const serverMsg = body?.error
|
|
171
|
+
throw new Error(serverMsg || `HTTP ${r.status}`)
|
|
172
|
+
}
|
|
169
173
|
return r.json()
|
|
170
174
|
})
|
|
171
175
|
.then(() => {
|
|
172
176
|
setCronsStatus('ok')
|
|
173
177
|
})
|
|
174
|
-
.catch(() => {
|
|
175
|
-
|
|
178
|
+
.catch((err: Error) => {
|
|
179
|
+
const msg = err.message || ''
|
|
180
|
+
if (msg.includes('Failed to fetch cron') || msg.includes('JSON') || msg.includes('Unexpected token')) {
|
|
181
|
+
setCronsError('Cron list failed -- OpenClaw CLI may be printing log output before JSON. Try: OPENCLAW_LOG_LEVEL=error clawport dev')
|
|
182
|
+
} else {
|
|
183
|
+
setCronsError('Could not reach OpenClaw gateway. Run: openclaw gateway run')
|
|
184
|
+
}
|
|
176
185
|
setCronsStatus('error')
|
|
177
186
|
})
|
|
178
187
|
}
|
package/lib/cli-utils.test.ts
CHANGED
|
@@ -41,4 +41,20 @@ hooks: Unrecognized key: "allowedAgentIds"
|
|
|
41
41
|
it('throws on empty string', () => {
|
|
42
42
|
expect(() => extractJson('')).toThrow()
|
|
43
43
|
})
|
|
44
|
+
|
|
45
|
+
it('skips bracketed log lines like [plugins] before real JSON', () => {
|
|
46
|
+
const raw = `[plugins] [debug] Database schema initialized
|
|
47
|
+
[plugins] [debug] Loading context engine
|
|
48
|
+
{"jobs":[{"name":"daily-report","schedule":"0 8 * * *"}]}`
|
|
49
|
+
const result = extractJson(raw) as Record<string, unknown>
|
|
50
|
+
expect(result.jobs).toEqual([{ name: 'daily-report', schedule: '0 8 * * *' }])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('skips bracketed log lines before JSON array', () => {
|
|
54
|
+
const raw = `[plugins] [debug] Database schema initialized
|
|
55
|
+
[plugins] [info] Ready
|
|
56
|
+
[{"id":"pulse","name":"daily-pulse"}]`
|
|
57
|
+
const result = extractJson(raw)
|
|
58
|
+
expect(result).toEqual([{ id: 'pulse', name: 'daily-pulse' }])
|
|
59
|
+
})
|
|
44
60
|
})
|
package/lib/cli-utils.ts
CHANGED
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Extract a JSON value from CLI output that may contain non-JSON preamble.
|
|
3
3
|
*
|
|
4
|
-
* Some OpenClaw versions print validation warnings
|
|
5
|
-
* to stdout before the JSON payload.
|
|
6
|
-
*
|
|
4
|
+
* Some OpenClaw versions print validation warnings or debug log lines
|
|
5
|
+
* (e.g. "[plugins] [debug] ...") to stdout before the JSON payload.
|
|
6
|
+
* This function finds the actual JSON structure by trying each `[` or `{`
|
|
7
|
+
* position until one parses successfully.
|
|
7
8
|
*/
|
|
8
9
|
export function extractJson(raw: string): unknown {
|
|
9
10
|
// Fast path: raw is already valid JSON
|
|
10
11
|
const trimmed = raw.trim()
|
|
11
12
|
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
12
|
-
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(trimmed)
|
|
15
|
+
} catch {
|
|
16
|
+
// May start with [ but be a log line like "[plugins] ..." -- fall through
|
|
17
|
+
}
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
// Try each potential JSON start position
|
|
21
|
+
let pos = 0
|
|
22
|
+
while (pos < raw.length) {
|
|
23
|
+
const arrStart = raw.indexOf('[', pos)
|
|
24
|
+
const objStart = raw.indexOf('{', pos)
|
|
25
|
+
const candidates = [arrStart, objStart].filter(i => i >= 0)
|
|
26
|
+
if (candidates.length === 0) break
|
|
27
|
+
|
|
28
|
+
const start = Math.min(...candidates)
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(raw.slice(start))
|
|
31
|
+
} catch {
|
|
32
|
+
// This wasn't the real JSON start -- advance past it
|
|
33
|
+
pos = start + 1
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
|
|
23
|
-
|
|
24
|
-
return JSON.parse(raw.slice(start))
|
|
37
|
+
throw new SyntaxError('No JSON found in CLI output')
|
|
25
38
|
}
|