clawport-ui 0.8.8 → 0.8.9

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.
@@ -331,6 +331,53 @@ describe('getMemoryStatus', () => {
331
331
  expect(status.lastIndexed).toBeNull()
332
332
  expect(status.totalEntries).toBeNull()
333
333
  })
334
+
335
+ it('parses new array-of-agents format from openclaw memory status --deep --json', () => {
336
+ mockExecSync.mockReturnValue(JSON.stringify([
337
+ {
338
+ agentId: 'main',
339
+ status: { files: 9, chunks: 13, dirty: false, provider: 'gemini', vector: { available: true } },
340
+ },
341
+ {
342
+ agentId: 'helper',
343
+ status: { files: 3, chunks: 5, dirty: false, provider: 'gemini', vector: { available: true } },
344
+ },
345
+ ]))
346
+
347
+ const status = getMemoryStatus()
348
+ expect(status.indexed).toBe(true)
349
+ expect(status.totalEntries).toBe(18) // 13 + 5 chunks
350
+ expect(status.vectorAvailable).toBe(true)
351
+ expect(status.embeddingProvider).toBe('gemini')
352
+ })
353
+
354
+ it('reports not indexed when any agent has dirty files', () => {
355
+ mockExecSync.mockReturnValue(JSON.stringify([
356
+ {
357
+ agentId: 'main',
358
+ status: { files: 9, chunks: 13, dirty: false, provider: 'gemini', vector: { available: true } },
359
+ },
360
+ {
361
+ agentId: 'helper',
362
+ status: { files: 3, chunks: 5, dirty: true, provider: 'gemini', vector: { available: true } },
363
+ },
364
+ ]))
365
+
366
+ const status = getMemoryStatus()
367
+ expect(status.indexed).toBe(false)
368
+ })
369
+
370
+ it('reports not indexed when an agent has zero files', () => {
371
+ mockExecSync.mockReturnValue(JSON.stringify([
372
+ {
373
+ agentId: 'main',
374
+ status: { files: 0, chunks: 0, dirty: false, provider: 'gemini', vector: { available: true } },
375
+ },
376
+ ]))
377
+
378
+ const status = getMemoryStatus()
379
+ expect(status.indexed).toBe(false)
380
+ })
334
381
  })
335
382
 
336
383
  // ── computeMemoryStats ──────────────────────────────────────────
package/lib/memory.ts CHANGED
@@ -3,6 +3,7 @@ import { readFileSync, existsSync, statSync, readdirSync } from 'fs'
3
3
  import { join, basename, dirname } from 'path'
4
4
  import { execSync } from 'child_process'
5
5
  import { requireEnv } from '@/lib/env'
6
+ import { extractJson } from '@/lib/cli-utils'
6
7
 
7
8
  // ── Date pattern for daily logs ─────────────────────────────────
8
9
 
@@ -213,21 +214,45 @@ export function getMemoryStatus(): MemoryStatus {
213
214
  }
214
215
 
215
216
  try {
216
- const output = execSync(`${bin} memory status --deep`, {
217
+ const output = execSync(`${bin} memory status --deep --json`, {
217
218
  timeout: 15000,
218
219
  encoding: 'utf-8',
219
220
  stdio: ['pipe', 'pipe', 'pipe'],
220
221
  }).trim()
221
222
 
222
- // Try JSON parse first
223
223
  try {
224
- const data = JSON.parse(output)
224
+ const data = extractJson(output)
225
+
226
+ // New format: array of per-agent status objects
227
+ // [{ agentId, status: { files, chunks, dirty, provider, vector } }, ...]
228
+ if (Array.isArray(data) && data.length > 0) {
229
+ const agents = data as { agentId?: string; status?: Record<string, unknown> }[]
230
+ const primary = agents[0].status ?? {}
231
+ const totalEntries = agents.reduce(
232
+ (sum, a) => sum + (typeof a.status?.chunks === 'number' ? a.status.chunks : 0), 0,
233
+ )
234
+ const allIndexed = agents.every(
235
+ a => (typeof a.status?.files === 'number' && a.status.files > 0) && !a.status?.dirty,
236
+ )
237
+ const vec = primary.vector as Record<string, unknown> | undefined
238
+ return {
239
+ indexed: allIndexed,
240
+ lastIndexed: null,
241
+ totalEntries,
242
+ vectorAvailable: typeof vec?.available === 'boolean' ? vec.available : null,
243
+ embeddingProvider: typeof primary.provider === 'string' ? primary.provider : null,
244
+ raw: output,
245
+ }
246
+ }
247
+
248
+ // Legacy flat object format
249
+ const flat = data as Record<string, unknown>
225
250
  return {
226
- indexed: data.indexed ?? false,
227
- lastIndexed: data.lastIndexed ?? null,
228
- totalEntries: data.totalEntries ?? null,
229
- vectorAvailable: data.vectorAvailable ?? null,
230
- embeddingProvider: data.embeddingProvider ?? null,
251
+ indexed: flat.indexed === true,
252
+ lastIndexed: typeof flat.lastIndexed === 'string' ? flat.lastIndexed : null,
253
+ totalEntries: typeof flat.totalEntries === 'number' ? flat.totalEntries : null,
254
+ vectorAvailable: typeof flat.vectorAvailable === 'boolean' ? flat.vectorAvailable : null,
255
+ embeddingProvider: typeof flat.embeddingProvider === 'string' ? flat.embeddingProvider : null,
231
256
  raw: output,
232
257
  }
233
258
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawport-ui",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "Open-source dashboard for managing, monitoring, and chatting with your OpenClaw AI agents.",
5
5
  "homepage": "https://clawport.dev",
6
6
  "repository": {