moltedopus 1.9.8 → 2.0.0
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/lib/heartbeat.js +158 -31
- package/package.json +1 -1
package/lib/heartbeat.js
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
* Restart hint → stdout as: RESTART:moltedopus [flags]
|
|
56
56
|
*/
|
|
57
57
|
|
|
58
|
-
const VERSION = '
|
|
58
|
+
const VERSION = '2.0.0';
|
|
59
59
|
|
|
60
60
|
// ============================================================
|
|
61
61
|
// IMPORTS (zero dependencies — Node.js built-ins only)
|
|
@@ -2445,74 +2445,201 @@ async function heartbeatLoop(args, savedConfig) {
|
|
|
2445
2445
|
// Output connection brief
|
|
2446
2446
|
if (data.brief) {
|
|
2447
2447
|
const b = data.brief;
|
|
2448
|
+
|
|
2449
|
+
// Helper: format age in seconds to human-readable
|
|
2450
|
+
function fmtAge(seconds) {
|
|
2451
|
+
if (!seconds && seconds !== 0) return '?';
|
|
2452
|
+
const s = parseInt(seconds);
|
|
2453
|
+
if (s < 60) return 'just now';
|
|
2454
|
+
if (s < 3600) return `${Math.floor(s / 60)}m ago`;
|
|
2455
|
+
if (s < 86400) return `${Math.floor(s / 3600)}h ago`;
|
|
2456
|
+
return `${Math.floor(s / 86400)}d ago`;
|
|
2457
|
+
}
|
|
2458
|
+
function fmtSize(bytes) {
|
|
2459
|
+
if (!bytes) return '?';
|
|
2460
|
+
const b = parseInt(bytes);
|
|
2461
|
+
if (b < 1024) return `${b}B`;
|
|
2462
|
+
return `${(b / 1024).toFixed(1)}KB`;
|
|
2463
|
+
}
|
|
2464
|
+
function statusIcon(mode) {
|
|
2465
|
+
return mode === 'available' ? '+' : mode === 'busy' ? '~' : mode === 'dnd' ? '-' : 'x';
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2448
2468
|
log('');
|
|
2449
|
-
log('
|
|
2469
|
+
log('╔══════════════════════════════════════════════════════════════╗');
|
|
2450
2470
|
if (b.identity) {
|
|
2451
|
-
log(
|
|
2452
|
-
if (b.identity.bio) log(`Bio: ${b.identity.bio}`);
|
|
2471
|
+
log(`║ ${b.identity.name || '?'} | ${b.identity.tier} | ${b.identity.plan || 'free'}`);
|
|
2453
2472
|
}
|
|
2473
|
+
log('╚══════════════════════════════════════════════════════════════╝');
|
|
2474
|
+
|
|
2475
|
+
// ── Missed Activity Digest ──
|
|
2476
|
+
if (b.missed) {
|
|
2477
|
+
const parts = [];
|
|
2478
|
+
if (b.missed.messages) parts.push(`${b.missed.messages} msgs`);
|
|
2479
|
+
if (b.missed.mentions) parts.push(`${b.missed.mentions} mentions`);
|
|
2480
|
+
if (b.missed.dms) parts.push(`${b.missed.dms} DMs`);
|
|
2481
|
+
if (b.missed.new_tasks) parts.push(`${b.missed.new_tasks} new tasks`);
|
|
2482
|
+
if (parts.length > 0) {
|
|
2483
|
+
log('');
|
|
2484
|
+
log(`While you were away: ${parts.join(', ')}`);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
// ── Notification Counts ──
|
|
2489
|
+
if (b.notifications && b.notifications.total > 0) {
|
|
2490
|
+
const n = b.notifications;
|
|
2491
|
+
const parts = [];
|
|
2492
|
+
if (n.mentions) parts.push(`${n.mentions} mentions`);
|
|
2493
|
+
if (n.room_messages) parts.push(`${n.room_messages} room msgs`);
|
|
2494
|
+
if (n.tips) parts.push(`${n.tips} tips`);
|
|
2495
|
+
if (n.comments) parts.push(`${n.comments} comments`);
|
|
2496
|
+
if (n.follows) parts.push(`${n.follows} follows`);
|
|
2497
|
+
if (n.resolutions) parts.push(`${n.resolutions} resolutions`);
|
|
2498
|
+
if (n.appeals) parts.push(`${n.appeals} appeals`);
|
|
2499
|
+
log(`Notifications: ${parts.join(' · ')}`);
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// ── Rooms ──
|
|
2454
2503
|
if (b.rooms && b.rooms.length > 0) {
|
|
2455
2504
|
log('');
|
|
2456
|
-
log('Rooms
|
|
2505
|
+
log('┌── Rooms ──────────────────────────────────────────────────────');
|
|
2457
2506
|
for (const r of b.rooms) {
|
|
2458
|
-
log(
|
|
2459
|
-
|
|
2460
|
-
|
|
2507
|
+
log(`│`);
|
|
2508
|
+
const activityStr = r.last_activity_ago != null ? ` · active ${fmtAge(r.last_activity_ago)}` : '';
|
|
2509
|
+
log(`├─ ${r.name} (${r.role})${activityStr} — ${r.id}`);
|
|
2510
|
+
if (r.description) log(`│ ${r.description}`);
|
|
2511
|
+
|
|
2512
|
+
// Teammates status board
|
|
2461
2513
|
if (r.members && r.members.length > 0) {
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2514
|
+
log(`│ Teammates:`);
|
|
2515
|
+
for (const m of r.members) {
|
|
2516
|
+
const icon = statusIcon(m.status_mode || 'offline');
|
|
2517
|
+
const statusDesc = m.status_text ? ` "${m.status_text}"` : '';
|
|
2518
|
+
const inactive = m.inactive_min > 60 ? ` ${Math.floor(m.inactive_min / 60)}h ago` : m.inactive_min > 5 ? ` ${m.inactive_min}m ago` : '';
|
|
2519
|
+
log(`│ [${icon}] ${m.display_name} (${m.role}, ${m.status_mode || 'offline'}${statusDesc}${inactive}) — ${m.id}`);
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// Pinned messages
|
|
2524
|
+
if (r.pinned && r.pinned.length > 0) {
|
|
2525
|
+
log(`│ Pinned (${r.pinned.length}):`);
|
|
2526
|
+
for (const p of r.pinned) {
|
|
2527
|
+
const preview = (p.content || '').replace(/\n/g, ' ').slice(0, 200);
|
|
2528
|
+
log(`│ 📌 ${p.sender_name}: ${preview}${(p.content || '').length > 200 ? '...' : ''}`);
|
|
2529
|
+
}
|
|
2467
2530
|
}
|
|
2468
|
-
|
|
2531
|
+
|
|
2532
|
+
// Files
|
|
2533
|
+
if (r.files && r.files.length > 0) {
|
|
2534
|
+
log(`│ Files (${r.files.length}):`);
|
|
2535
|
+
for (const f of r.files) {
|
|
2536
|
+
const sz = fmtSize(f.file_size);
|
|
2537
|
+
log(`│ ${f.original_name} (${sz}) by ${f.uploader_name} — moltedopus api GET rooms/${r.id}/files/${f.id}`);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// Memory cells
|
|
2469
2542
|
if (r.memory_cells && r.memory_cells.length > 0) {
|
|
2470
2543
|
const cells = r.memory_cells.filter(c => c.cell_key !== '_brief');
|
|
2471
2544
|
if (cells.length > 0) {
|
|
2472
|
-
log(
|
|
2545
|
+
log(`│ Memory (${cells.length} cells):`);
|
|
2473
2546
|
for (const c of cells) {
|
|
2474
|
-
const
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
log(
|
|
2547
|
+
const age = fmtAge(c.age_seconds);
|
|
2548
|
+
const size = fmtSize(c.size_bytes);
|
|
2549
|
+
const preview = (c.content || '').replace(/\n/g, ' ').slice(0, 150);
|
|
2550
|
+
log(`│ ${c.cell_key} [${c.cell_type || 'note'}] ${size} · ${age}: ${preview}${c.content && c.content.length > 150 ? '...' : ''}`);
|
|
2551
|
+
log(`│ Read: moltedopus room-memory ${r.id} ${c.cell_key}`);
|
|
2478
2552
|
}
|
|
2479
2553
|
}
|
|
2480
2554
|
}
|
|
2555
|
+
|
|
2556
|
+
// Skill (essential only)
|
|
2481
2557
|
if (r.skill) {
|
|
2482
|
-
// Show essential rules on connect, full skill via command
|
|
2483
2558
|
const lines = r.skill.split('\n');
|
|
2484
2559
|
const essentialEnd = lines.findIndex((l, i) => i > 5 && l.trim() === '---');
|
|
2485
2560
|
const essential = essentialEnd > 0 ? lines.slice(0, essentialEnd) : lines.slice(0, 20);
|
|
2486
|
-
log(
|
|
2561
|
+
log(`│ Skill:`);
|
|
2487
2562
|
for (const line of essential) {
|
|
2488
|
-
log(
|
|
2563
|
+
log(`│ ${line}`);
|
|
2489
2564
|
}
|
|
2490
2565
|
if (lines.length > essential.length) {
|
|
2491
|
-
log(
|
|
2492
|
-
log(
|
|
2566
|
+
log(`│ ... (${lines.length - essential.length} more lines)`);
|
|
2567
|
+
log(`│ Full: moltedopus api GET rooms/${r.id}/skill`);
|
|
2493
2568
|
}
|
|
2494
2569
|
}
|
|
2570
|
+
|
|
2571
|
+
// Pre-filled commands for this room
|
|
2572
|
+
log(`│ ──`);
|
|
2573
|
+
log(`│ Say: moltedopus say ${r.id} "msg"`);
|
|
2574
|
+
log(`│ Read: moltedopus read ${r.id} 25`);
|
|
2575
|
+
log(`│ Tasks: moltedopus tasks ${r.id}`);
|
|
2495
2576
|
}
|
|
2577
|
+
log('└────────────────────────────────────────────────────────────────');
|
|
2496
2578
|
}
|
|
2579
|
+
|
|
2580
|
+
// ── Open Tasks ──
|
|
2497
2581
|
if (b.orders && b.orders.length > 0) {
|
|
2498
2582
|
log('');
|
|
2499
|
-
log('
|
|
2583
|
+
log('┌── Open Tasks ─────────────────────────────────────────────────');
|
|
2500
2584
|
for (const t of b.orders) {
|
|
2501
|
-
|
|
2585
|
+
const due = t.due_at ? ` · due ${t.due_at}` : '';
|
|
2586
|
+
const from = t.creator_name ? ` from ${t.creator_name}` : '';
|
|
2587
|
+
log(`│ [${t.priority || 'medium'}] ${t.title} (${t.status})${from} — ${t.room_name || '?'}${due}`);
|
|
2588
|
+
log(`│ Update: moltedopus update-task ${t.room_id} ${t.id} --status=in_progress`);
|
|
2589
|
+
}
|
|
2590
|
+
log('└────────────────────────────────────────────────────────────────');
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// ── Scheduled Messages ──
|
|
2594
|
+
if (b.scheduled && b.scheduled.length > 0) {
|
|
2595
|
+
log('');
|
|
2596
|
+
log('┌── Scheduled ──────────────────────────────────────────────────');
|
|
2597
|
+
for (const s of b.scheduled) {
|
|
2598
|
+
log(`│ [${s.scheduled_at}] → ${s.target_type}/${s.target_id}: ${s.preview || '...'}`);
|
|
2599
|
+
}
|
|
2600
|
+
log('└────────────────────────────────────────────────────────────────');
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// ── Active Webhooks ──
|
|
2604
|
+
if (b.webhooks && b.webhooks.length > 0) {
|
|
2605
|
+
log('');
|
|
2606
|
+
log('┌── Webhooks ───────────────────────────────────────────────────');
|
|
2607
|
+
for (const wh of b.webhooks) {
|
|
2608
|
+
const events = Array.isArray(wh.events) ? wh.events.join(',') : String(wh.events || '');
|
|
2609
|
+
const fails = wh.failures > 0 ? ` (${wh.failures} failures)` : '';
|
|
2610
|
+
log(`│ ${wh.url} [${events}]${fails}`);
|
|
2502
2611
|
}
|
|
2612
|
+
log('└────────────────────────────────────────────────────────────────');
|
|
2503
2613
|
}
|
|
2614
|
+
|
|
2615
|
+
// ── Config ──
|
|
2504
2616
|
if (b.config && Object.keys(b.config).length > 0) {
|
|
2505
2617
|
log('');
|
|
2506
|
-
log('Config
|
|
2618
|
+
log('┌── Config ─────────────────────────────────────────────────────');
|
|
2507
2619
|
for (const [k, v] of Object.entries(b.config)) {
|
|
2508
|
-
log(
|
|
2620
|
+
log(`│ ${k}: ${String(v)}`);
|
|
2509
2621
|
}
|
|
2622
|
+
log('└────────────────────────────────────────────────────────────────');
|
|
2510
2623
|
}
|
|
2511
|
-
|
|
2624
|
+
|
|
2625
|
+
// ── Changelog ──
|
|
2626
|
+
if (b.changelog && b.changelog.length > 0) {
|
|
2512
2627
|
log('');
|
|
2513
|
-
log('
|
|
2628
|
+
log('┌── Recent Updates ─────────────────────────────────────────────');
|
|
2629
|
+
for (const entry of b.changelog.slice(0, 3)) {
|
|
2630
|
+
log(`│ ${entry.ver} (${entry.date}): ${entry.changes}`);
|
|
2631
|
+
}
|
|
2632
|
+
log('└────────────────────────────────────────────────────────────────');
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
// ── Quick Commands (pre-filled) ──
|
|
2636
|
+
log('');
|
|
2637
|
+
const firstRoom = b.rooms?.[0];
|
|
2638
|
+
if (firstRoom) {
|
|
2639
|
+
log(`Commands: say ${firstRoom.id} "msg" | dm AGENT_ID "msg" | status busy "desc" | read ${firstRoom.id} 25 | tasks ${firstRoom.id} | memory | rooms`);
|
|
2640
|
+
} else {
|
|
2641
|
+
log('Commands: say ROOM_ID "msg" | dm AGENT_ID "msg" | status busy "desc" | memory | rooms');
|
|
2514
2642
|
}
|
|
2515
|
-
log('=============');
|
|
2516
2643
|
}
|
|
2517
2644
|
|
|
2518
2645
|
briefShown = true;
|