clawport-ui 0.5.0 → 0.5.1

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.
@@ -1,4 +1,5 @@
1
1
  import { readFileSync, existsSync, readdirSync } from 'fs'
2
+ import { execSync } from 'child_process'
2
3
  import { join, basename } from 'path'
3
4
  import bundledRegistry from '@/lib/agents.json'
4
5
  import type { Agent } from '@/lib/types'
@@ -295,16 +296,100 @@ function discoverAgents(workspacePath: string): AgentEntry[] | null {
295
296
  return discovered.length > 0 ? discovered : null
296
297
  }
297
298
 
299
+ // ---------------------------------------------------------------------------
300
+ // CLI-based discovery (openclaw agents list)
301
+ // ---------------------------------------------------------------------------
302
+
303
+ /**
304
+ * Discover agents via `openclaw agents list --json`.
305
+ * Returns AgentEntry[] for any agents found, or null on failure.
306
+ * This catches agents defined in openclaw.json that don't have
307
+ * filesystem directories under agents/.
308
+ */
309
+ function discoverAgentsViaCli(openclawBin: string): AgentEntry[] | null {
310
+ try {
311
+ const raw = execSync(`${openclawBin} agents list --json`, {
312
+ encoding: 'utf-8',
313
+ timeout: 10000,
314
+ })
315
+ const parsed = JSON.parse(raw)
316
+ const agents: unknown[] = Array.isArray(parsed)
317
+ ? parsed
318
+ : parsed.agents ?? parsed.data ?? []
319
+
320
+ if (agents.length === 0) return null
321
+
322
+ let colorIndex = 0
323
+ return agents.map((entry) => {
324
+ const a = entry as Record<string, unknown>
325
+ const id = String(a.id ?? a.name ?? a.slug ?? '')
326
+ const name = String(a.name ?? a.displayName ?? slugToName(id))
327
+ const title = String(a.title ?? a.role ?? 'Agent')
328
+ const reportsTo = a.reportsTo != null ? String(a.reportsTo) : null
329
+ const directReports = Array.isArray(a.directReports)
330
+ ? a.directReports.map(String)
331
+ : []
332
+ const tools = Array.isArray(a.tools) ? a.tools.map(String) : ['read', 'write']
333
+
334
+ return {
335
+ id,
336
+ name,
337
+ title,
338
+ reportsTo,
339
+ directReports,
340
+ soulPath: typeof a.soulPath === 'string' ? a.soulPath : null,
341
+ voiceId: typeof a.voiceId === 'string' ? a.voiceId : null,
342
+ color: typeof a.color === 'string' ? a.color : DISCOVER_COLORS[colorIndex++ % DISCOVER_COLORS.length],
343
+ emoji: typeof a.emoji === 'string' ? a.emoji : name.charAt(0).toUpperCase(),
344
+ tools,
345
+ memoryPath: typeof a.memoryPath === 'string' ? a.memoryPath : null,
346
+ description: typeof a.description === 'string' ? a.description : `${name} agent.`,
347
+ }
348
+ })
349
+ } catch {
350
+ return null
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Merge CLI-discovered agents into an existing registry.
356
+ * Only adds agents whose IDs are not already present.
357
+ * Also patches directReports on existing agents to include new CLI agents.
358
+ */
359
+ function mergeCliAgents(existing: AgentEntry[], cliAgents: AgentEntry[]): AgentEntry[] {
360
+ const existingIds = new Set(existing.map(a => a.id))
361
+ const added: AgentEntry[] = []
362
+
363
+ for (const ca of cliAgents) {
364
+ if (existingIds.has(ca.id)) continue
365
+ added.push(ca)
366
+ existingIds.add(ca.id)
367
+
368
+ // If the CLI agent reports to an existing agent, add it to that agent's directReports
369
+ if (ca.reportsTo) {
370
+ const parent = existing.find(a => a.id === ca.reportsTo)
371
+ if (parent && !parent.directReports.includes(ca.id)) {
372
+ parent.directReports.push(ca.id)
373
+ }
374
+ }
375
+ }
376
+
377
+ return [...existing, ...added]
378
+ }
379
+
298
380
  /**
299
381
  * Load the agent registry.
300
382
  *
301
383
  * Resolution order:
302
384
  * 1. $WORKSPACE_PATH/clawport/agents.json (user's own config)
303
385
  * 2. Auto-discovered from $WORKSPACE_PATH (agents/ directory scan)
304
- * 3. Bundled lib/agents.json (default example registry)
386
+ * + merged with `openclaw agents list --json` (catches config-only agents)
387
+ * 3. CLI-only discovery via `openclaw agents list --json`
388
+ * 4. Bundled lib/agents.json (default example registry)
305
389
  */
306
390
  export function loadRegistry(): AgentEntry[] {
307
391
  const workspacePath = process.env.WORKSPACE_PATH
392
+ const openclawBin = process.env.OPENCLAW_BIN
308
393
 
309
394
  if (workspacePath) {
310
395
  // 1. User-provided override
@@ -318,11 +403,24 @@ export function loadRegistry(): AgentEntry[] {
318
403
  }
319
404
  }
320
405
 
321
- // 2. Auto-discover from workspace
406
+ // 2. Auto-discover from workspace filesystem
322
407
  const discovered = discoverAgents(workspacePath)
408
+
409
+ // 2b. Merge in any agents known to OpenClaw CLI but missing from filesystem
410
+ if (discovered && openclawBin) {
411
+ const cliAgents = discoverAgentsViaCli(openclawBin)
412
+ if (cliAgents) return mergeCliAgents(discovered, cliAgents)
413
+ return discovered
414
+ }
323
415
  if (discovered) return discovered
416
+
417
+ // 3. CLI-only discovery (no workspace agents/ dir, but CLI knows about agents)
418
+ if (openclawBin) {
419
+ const cliAgents = discoverAgentsViaCli(openclawBin)
420
+ if (cliAgents) return cliAgents
421
+ }
324
422
  }
325
423
 
326
- // 3. Bundled fallback
424
+ // 4. Bundled fallback
327
425
  return bundledRegistry as AgentEntry[]
328
426
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawport-ui",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
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": {