fix(btmsg): convert positional column access to named, fix camelCase mismatch
CRITICAL: get_agents() used SELECT * positional index 7 for status, but column 7 is system_prompt (column 8 is status). Converted all query functions in btmsg.rs and bttask.rs to named column access. Fixed BtmsgAgent/BtmsgMessage TypeScript interfaces to use camelCase matching Rust serde(rename_all = camelCase). Updated CommsTab consumer.
This commit is contained in:
parent
32f6d7eadf
commit
93c2cdf434
4 changed files with 99 additions and 88 deletions
|
|
@ -1,4 +1,4 @@
|
|||
// btmsg — Read-only access to btmsg SQLite database
|
||||
// btmsg — Access to btmsg SQLite database
|
||||
// Database at ~/.local/share/bterminal/btmsg.db (created by btmsg CLI)
|
||||
|
||||
use rusqlite::{params, Connection, OpenFlags};
|
||||
|
|
@ -17,8 +17,13 @@ fn open_db() -> Result<Connection, String> {
|
|||
if !path.exists() {
|
||||
return Err("btmsg database not found. Run 'btmsg register' first.".into());
|
||||
}
|
||||
Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)
|
||||
.map_err(|e| format!("Failed to open btmsg.db: {e}"))
|
||||
let conn = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)
|
||||
.map_err(|e| format!("Failed to open btmsg.db: {e}"))?;
|
||||
conn.pragma_update(None, "journal_mode", "WAL")
|
||||
.map_err(|e| format!("Failed to set WAL mode: {e}"))?;
|
||||
conn.pragma_update(None, "busy_timeout", 5000)
|
||||
.map_err(|e| format!("Failed to set busy_timeout: {e}"))?;
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
@ -95,13 +100,13 @@ pub fn get_agents(group_id: &str) -> Result<Vec<BtmsgAgent>, String> {
|
|||
|
||||
let agents = stmt.query_map(params![group_id], |row| {
|
||||
Ok(BtmsgAgent {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
role: row.get(2)?,
|
||||
group_id: row.get(3)?,
|
||||
tier: row.get(4)?,
|
||||
model: row.get(5)?,
|
||||
status: row.get::<_, Option<String>>(7)?.unwrap_or_else(|| "stopped".into()),
|
||||
id: row.get("id")?,
|
||||
name: row.get("name")?,
|
||||
role: row.get("role")?,
|
||||
group_id: row.get("group_id")?,
|
||||
tier: row.get("tier")?,
|
||||
model: row.get("model")?,
|
||||
status: row.get::<_, Option<String>>("status")?.unwrap_or_else(|| "stopped".into()),
|
||||
unread_count: row.get("unread_count")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
|
@ -122,22 +127,22 @@ pub fn unread_messages(agent_id: &str) -> Result<Vec<BtmsgMessage>, String> {
|
|||
let db = open_db()?;
|
||||
let mut stmt = db.prepare(
|
||||
"SELECT m.id, m.from_agent, m.to_agent, m.content, m.read, m.reply_to, m.created_at, \
|
||||
a.name, a.role \
|
||||
a.name AS sender_name, a.role AS sender_role \
|
||||
FROM messages m JOIN agents a ON m.from_agent = a.id \
|
||||
WHERE m.to_agent = ? AND m.read = 0 ORDER BY m.created_at ASC"
|
||||
).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
let msgs = stmt.query_map(params![agent_id], |row| {
|
||||
Ok(BtmsgMessage {
|
||||
id: row.get(0)?,
|
||||
from_agent: row.get(1)?,
|
||||
to_agent: row.get(2)?,
|
||||
content: row.get(3)?,
|
||||
read: row.get::<_, i32>(4)? != 0,
|
||||
reply_to: row.get(5)?,
|
||||
created_at: row.get(6)?,
|
||||
sender_name: row.get(7)?,
|
||||
sender_role: row.get(8)?,
|
||||
id: row.get("id")?,
|
||||
from_agent: row.get("from_agent")?,
|
||||
to_agent: row.get("to_agent")?,
|
||||
content: row.get("content")?,
|
||||
read: row.get::<_, i32>("read")? != 0,
|
||||
reply_to: row.get("reply_to")?,
|
||||
created_at: row.get("created_at")?,
|
||||
sender_name: row.get("sender_name")?,
|
||||
sender_role: row.get("sender_role")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
|
|
@ -148,7 +153,7 @@ pub fn history(agent_id: &str, other_id: &str, limit: i32) -> Result<Vec<BtmsgMe
|
|||
let db = open_db()?;
|
||||
let mut stmt = db.prepare(
|
||||
"SELECT m.id, m.from_agent, m.to_agent, m.content, m.read, m.reply_to, m.created_at, \
|
||||
a.name, a.role \
|
||||
a.name AS sender_name, a.role AS sender_role \
|
||||
FROM messages m JOIN agents a ON m.from_agent = a.id \
|
||||
WHERE (m.from_agent = ?1 AND m.to_agent = ?2) OR (m.from_agent = ?2 AND m.to_agent = ?1) \
|
||||
ORDER BY m.created_at ASC LIMIT ?3"
|
||||
|
|
@ -156,15 +161,15 @@ pub fn history(agent_id: &str, other_id: &str, limit: i32) -> Result<Vec<BtmsgMe
|
|||
|
||||
let msgs = stmt.query_map(params![agent_id, other_id, limit], |row| {
|
||||
Ok(BtmsgMessage {
|
||||
id: row.get(0)?,
|
||||
from_agent: row.get(1)?,
|
||||
to_agent: row.get(2)?,
|
||||
content: row.get(3)?,
|
||||
read: row.get::<_, i32>(4)? != 0,
|
||||
reply_to: row.get(5)?,
|
||||
created_at: row.get(6)?,
|
||||
sender_name: row.get(7)?,
|
||||
sender_role: row.get(8)?,
|
||||
id: row.get("id")?,
|
||||
from_agent: row.get("from_agent")?,
|
||||
to_agent: row.get("to_agent")?,
|
||||
content: row.get("content")?,
|
||||
read: row.get::<_, i32>("read")? != 0,
|
||||
reply_to: row.get("reply_to")?,
|
||||
created_at: row.get("created_at")?,
|
||||
sender_name: row.get("sender_name")?,
|
||||
sender_role: row.get("sender_role")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
|
|
@ -257,7 +262,8 @@ pub fn all_feed(group_id: &str, limit: i32) -> Result<Vec<BtmsgFeedMessage>, Str
|
|||
let db = open_db()?;
|
||||
let mut stmt = db.prepare(
|
||||
"SELECT m.id, m.from_agent, m.to_agent, m.content, m.created_at, m.reply_to, \
|
||||
a1.name, a1.role, a2.name, a2.role \
|
||||
a1.name AS sender_name, a1.role AS sender_role, \
|
||||
a2.name AS recipient_name, a2.role AS recipient_role \
|
||||
FROM messages m \
|
||||
JOIN agents a1 ON m.from_agent = a1.id \
|
||||
JOIN agents a2 ON m.to_agent = a2.id \
|
||||
|
|
@ -267,16 +273,16 @@ pub fn all_feed(group_id: &str, limit: i32) -> Result<Vec<BtmsgFeedMessage>, Str
|
|||
|
||||
let msgs = stmt.query_map(params![group_id, limit], |row| {
|
||||
Ok(BtmsgFeedMessage {
|
||||
id: row.get(0)?,
|
||||
from_agent: row.get(1)?,
|
||||
to_agent: row.get(2)?,
|
||||
content: row.get(3)?,
|
||||
created_at: row.get(4)?,
|
||||
reply_to: row.get(5)?,
|
||||
sender_name: row.get(6)?,
|
||||
sender_role: row.get(7)?,
|
||||
recipient_name: row.get(8)?,
|
||||
recipient_role: row.get(9)?,
|
||||
id: row.get("id")?,
|
||||
from_agent: row.get("from_agent")?,
|
||||
to_agent: row.get("to_agent")?,
|
||||
content: row.get("content")?,
|
||||
created_at: row.get("created_at")?,
|
||||
reply_to: row.get("reply_to")?,
|
||||
sender_name: row.get("sender_name")?,
|
||||
sender_role: row.get("sender_role")?,
|
||||
recipient_name: row.get("recipient_name")?,
|
||||
recipient_role: row.get("recipient_role")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
|
|
@ -296,19 +302,19 @@ pub fn get_channels(group_id: &str) -> Result<Vec<BtmsgChannel>, String> {
|
|||
let db = open_db()?;
|
||||
let mut stmt = db.prepare(
|
||||
"SELECT c.id, c.name, c.group_id, c.created_by, \
|
||||
(SELECT COUNT(*) FROM channel_members cm WHERE cm.channel_id = c.id), \
|
||||
(SELECT COUNT(*) FROM channel_members cm WHERE cm.channel_id = c.id) AS member_count, \
|
||||
c.created_at \
|
||||
FROM channels c WHERE c.group_id = ? ORDER BY c.name"
|
||||
).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
let channels = stmt.query_map(params![group_id], |row| {
|
||||
Ok(BtmsgChannel {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
group_id: row.get(2)?,
|
||||
created_by: row.get(3)?,
|
||||
member_count: row.get(4)?,
|
||||
created_at: row.get(5)?,
|
||||
id: row.get("id")?,
|
||||
name: row.get("name")?,
|
||||
group_id: row.get("group_id")?,
|
||||
created_by: row.get("created_by")?,
|
||||
member_count: row.get("member_count")?,
|
||||
created_at: row.get("created_at")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
|
|
@ -319,20 +325,20 @@ pub fn get_channel_messages(channel_id: &str, limit: i32) -> Result<Vec<BtmsgCha
|
|||
let db = open_db()?;
|
||||
let mut stmt = db.prepare(
|
||||
"SELECT cm.id, cm.channel_id, cm.from_agent, cm.content, cm.created_at, \
|
||||
a.name, a.role \
|
||||
a.name AS sender_name, a.role AS sender_role \
|
||||
FROM channel_messages cm JOIN agents a ON cm.from_agent = a.id \
|
||||
WHERE cm.channel_id = ? ORDER BY cm.created_at ASC LIMIT ?"
|
||||
).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
let msgs = stmt.query_map(params![channel_id, limit], |row| {
|
||||
Ok(BtmsgChannelMessage {
|
||||
id: row.get(0)?,
|
||||
channel_id: row.get(1)?,
|
||||
from_agent: row.get(2)?,
|
||||
content: row.get(3)?,
|
||||
created_at: row.get(4)?,
|
||||
sender_name: row.get(5)?,
|
||||
sender_role: row.get(6)?,
|
||||
id: row.get("id")?,
|
||||
channel_id: row.get("channel_id")?,
|
||||
from_agent: row.get("from_agent")?,
|
||||
content: row.get("content")?,
|
||||
created_at: row.get("created_at")?,
|
||||
sender_name: row.get("sender_name")?,
|
||||
sender_role: row.get("sender_role")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query error: {e}"))?;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,13 @@ fn open_db() -> Result<Connection, String> {
|
|||
if !path.exists() {
|
||||
return Err("btmsg database not found".into());
|
||||
}
|
||||
Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)
|
||||
.map_err(|e| format!("Failed to open btmsg.db: {e}"))
|
||||
let conn = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)
|
||||
.map_err(|e| format!("Failed to open btmsg.db: {e}"))?;
|
||||
conn.pragma_update(None, "journal_mode", "WAL")
|
||||
.map_err(|e| format!("Failed to set WAL mode: {e}"))?;
|
||||
conn.pragma_update(None, "busy_timeout", 5000)
|
||||
.map_err(|e| format!("Failed to set busy_timeout: {e}"))?;
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
@ -64,18 +69,18 @@ pub fn list_tasks(group_id: &str) -> Result<Vec<Task>, String> {
|
|||
let rows = stmt
|
||||
.query_map(params![group_id], |row| {
|
||||
Ok(Task {
|
||||
id: row.get(0)?,
|
||||
title: row.get(1)?,
|
||||
description: row.get::<_, String>(2).unwrap_or_default(),
|
||||
status: row.get::<_, String>(3).unwrap_or_else(|_| "todo".into()),
|
||||
priority: row.get::<_, String>(4).unwrap_or_else(|_| "medium".into()),
|
||||
assigned_to: row.get(5)?,
|
||||
created_by: row.get(6)?,
|
||||
group_id: row.get(7)?,
|
||||
parent_task_id: row.get(8)?,
|
||||
sort_order: row.get::<_, i32>(9).unwrap_or(0),
|
||||
created_at: row.get::<_, String>(10).unwrap_or_default(),
|
||||
updated_at: row.get::<_, String>(11).unwrap_or_default(),
|
||||
id: row.get("id")?,
|
||||
title: row.get("title")?,
|
||||
description: row.get::<_, String>("description").unwrap_or_default(),
|
||||
status: row.get::<_, String>("status").unwrap_or_else(|_| "todo".into()),
|
||||
priority: row.get::<_, String>("priority").unwrap_or_else(|_| "medium".into()),
|
||||
assigned_to: row.get("assigned_to")?,
|
||||
created_by: row.get("created_by")?,
|
||||
group_id: row.get("group_id")?,
|
||||
parent_task_id: row.get("parent_task_id")?,
|
||||
sort_order: row.get::<_, i32>("sort_order").unwrap_or(0),
|
||||
created_at: row.get::<_, String>("created_at").unwrap_or_default(),
|
||||
updated_at: row.get::<_, String>("updated_at").unwrap_or_default(),
|
||||
})
|
||||
})
|
||||
.map_err(|e| format!("Query error: {e}"))?;
|
||||
|
|
@ -98,11 +103,11 @@ pub fn task_comments(task_id: &str) -> Result<Vec<TaskComment>, String> {
|
|||
let rows = stmt
|
||||
.query_map(params![task_id], |row| {
|
||||
Ok(TaskComment {
|
||||
id: row.get(0)?,
|
||||
task_id: row.get(1)?,
|
||||
agent_id: row.get(2)?,
|
||||
content: row.get(3)?,
|
||||
created_at: row.get::<_, String>(4).unwrap_or_default(),
|
||||
id: row.get("id")?,
|
||||
task_id: row.get("task_id")?,
|
||||
agent_id: row.get("agent_id")?,
|
||||
content: row.get("content")?,
|
||||
created_at: row.get::<_, String>("created_at").unwrap_or_default(),
|
||||
})
|
||||
})
|
||||
.map_err(|e| format!("Query error: {e}"))?;
|
||||
|
|
|
|||
|
|
@ -10,23 +10,23 @@ export interface BtmsgAgent {
|
|||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
group_id: string;
|
||||
groupId: string;
|
||||
tier: number;
|
||||
model: string | null;
|
||||
status: string;
|
||||
unread_count: number;
|
||||
unreadCount: number;
|
||||
}
|
||||
|
||||
export interface BtmsgMessage {
|
||||
id: string;
|
||||
from_agent: string;
|
||||
to_agent: string;
|
||||
fromAgent: string;
|
||||
toAgent: string;
|
||||
content: string;
|
||||
read: boolean;
|
||||
reply_to: string | null;
|
||||
created_at: string;
|
||||
sender_name?: string;
|
||||
sender_role?: string;
|
||||
replyTo: string | null;
|
||||
createdAt: string;
|
||||
senderName?: string;
|
||||
senderRole?: string;
|
||||
}
|
||||
|
||||
export interface BtmsgFeedMessage {
|
||||
|
|
|
|||
|
|
@ -257,8 +257,8 @@
|
|||
<span class="conv-icon">{getAgentIcon(agent.role)}</span>
|
||||
<span class="conv-name">{agent.name}</span>
|
||||
<span class="status-dot {statusClass}"></span>
|
||||
{#if agent.unread_count > 0}
|
||||
<span class="unread-badge">{agent.unread_count}</span>
|
||||
{#if agent.unreadCount > 0}
|
||||
<span class="unread-badge">{agent.unreadCount}</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
|
|
@ -301,11 +301,11 @@
|
|||
<div class="empty-state">No messages yet. Start the conversation!</div>
|
||||
{:else}
|
||||
{#each dmMessages as msg (msg.id)}
|
||||
{@const isMe = msg.from_agent === ADMIN_ID}
|
||||
{@const isMe = msg.fromAgent === ADMIN_ID}
|
||||
<div class="message" class:own={isMe}>
|
||||
<div class="msg-header">
|
||||
<span class="msg-sender">{isMe ? 'You' : (msg.sender_name ?? msg.from_agent)}</span>
|
||||
<span class="msg-time">{formatTime(msg.created_at)}</span>
|
||||
<span class="msg-sender">{isMe ? 'You' : (msg.senderName ?? msg.fromAgent)}</span>
|
||||
<span class="msg-time">{formatTime(msg.createdAt)}</span>
|
||||
</div>
|
||||
<div class="msg-content">{msg.content}</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue