64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
/**
|
|
* Host-side command gate. Classifies inbound slash commands and gates
|
|
* them before they reach the container.
|
|
*
|
|
* - Filtered commands: dropped silently (never reach the container)
|
|
* - Admin commands: checked against user_roles; denied senders get a
|
|
* "Permission denied" response written directly to messages_out
|
|
* - Normal messages: pass through unchanged
|
|
*/
|
|
import { getDb, hasTable } from './db/connection.js';
|
|
|
|
export type GateResult = { action: 'pass' } | { action: 'filter' } | { action: 'deny'; command: string };
|
|
|
|
const FILTERED_COMMANDS = new Set(['/help', '/login', '/logout', '/doctor', '/config', '/remote-control']);
|
|
const ADMIN_COMMANDS = new Set(['/clear', '/compact', '/context', '/cost', '/files']);
|
|
|
|
/**
|
|
* Classify a message and decide whether it should reach the container.
|
|
* Returns 'pass' for normal messages and authorized admin commands,
|
|
* 'filter' for silently-dropped commands, 'deny' for unauthorized
|
|
* admin commands.
|
|
*/
|
|
export function gateCommand(content: string, userId: string | null, agentGroupId: string): GateResult {
|
|
let text: string;
|
|
try {
|
|
const parsed = JSON.parse(content);
|
|
text = (parsed.text || '').trim();
|
|
} catch {
|
|
text = content.trim();
|
|
}
|
|
|
|
if (!text.startsWith('/')) return { action: 'pass' };
|
|
|
|
const command = text.split(/\s/)[0].toLowerCase();
|
|
|
|
if (FILTERED_COMMANDS.has(command)) return { action: 'filter' };
|
|
|
|
if (ADMIN_COMMANDS.has(command)) {
|
|
if (isAdmin(userId, agentGroupId)) {
|
|
return { action: 'pass' };
|
|
}
|
|
return { action: 'deny', command };
|
|
}
|
|
|
|
// Unknown slash commands pass through (the agent/SDK handles them)
|
|
return { action: 'pass' };
|
|
}
|
|
|
|
function isAdmin(userId: string | null, agentGroupId: string): boolean {
|
|
if (!userId) return false;
|
|
if (!hasTable(getDb(), 'user_roles')) return true; // no permissions module = allow all
|
|
const db = getDb();
|
|
const row = db
|
|
.prepare(
|
|
`SELECT 1 FROM user_roles
|
|
WHERE user_id = ?
|
|
AND (role = 'owner' OR role = 'admin')
|
|
AND (agent_group_id IS NULL OR agent_group_id = ?)
|
|
LIMIT 1`,
|
|
)
|
|
.get(userId, agentGroupId);
|
|
return row != null;
|
|
}
|