@dailyautomations/terminal-logger
Version:
Terminal command logger with Supabase sync for swarm prompts
247 lines (244 loc) • 9.03 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SupabaseSync = void 0;
const supabase_js_1 = require("@supabase/supabase-js");
const logger_1 = require("./logger");
const cron = __importStar(require("node-cron"));
class SupabaseSync {
client;
syncTask;
syncStatus;
constructor(url, anonKey) {
if (url && anonKey) {
this.client = (0, supabase_js_1.createClient)(url, anonKey);
}
this.syncStatus = {
lastSyncTime: new Date(0),
pendingCommands: 0,
syncErrors: 0,
isEnabled: !!this.client
};
}
async initialize() {
if (!this.client) {
logger_1.logger.warn('Supabase sync disabled - no credentials provided');
return;
}
try {
await this.createTablesIfNotExist();
logger_1.logger.info('Supabase sync initialized');
}
catch (error) {
logger_1.logger.error('Failed to initialize Supabase sync', error);
this.syncStatus.syncErrors++;
}
}
async createTablesIfNotExist() {
if (!this.client)
return;
// Note: In production, you'd create these tables via Supabase dashboard or migration
// This is just a placeholder to show the expected schema
const schema = `
-- Terminal Commands Table
CREATE TABLE IF NOT EXISTS terminal_commands (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
command_id TEXT UNIQUE NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
command TEXT NOT NULL,
directory TEXT NOT NULL,
user_name TEXT NOT NULL,
session_id TEXT NOT NULL,
hostname TEXT NOT NULL,
exit_code INTEGER,
duration INTEGER,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Sessions Table
CREATE TABLE IF NOT EXISTS terminal_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id TEXT UNIQUE NOT NULL,
start_time TIMESTAMPTZ NOT NULL,
end_time TIMESTAMPTZ,
user_name TEXT NOT NULL,
hostname TEXT NOT NULL,
shell TEXT NOT NULL,
terminal TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_commands_timestamp ON terminal_commands(timestamp);
CREATE INDEX IF NOT EXISTS idx_commands_session ON terminal_commands(session_id);
CREATE INDEX IF NOT EXISTS idx_commands_metadata ON terminal_commands(metadata);
`;
logger_1.logger.info('Database schema ready');
}
async syncCommands(commands) {
if (!this.client || commands.length === 0)
return;
try {
logger_1.logger.info(`Syncing ${commands.length} commands to Supabase`);
// Transform commands for Supabase
const supabaseCommands = commands.map(cmd => ({
command_id: cmd.id,
timestamp: cmd.timestamp,
command: cmd.command,
directory: cmd.directory,
user_name: cmd.user,
session_id: cmd.sessionId,
hostname: cmd.hostname,
exit_code: cmd.exitCode,
duration: cmd.duration,
metadata: cmd.metadata
}));
const { error } = await this.client
.from('terminal_commands')
.upsert(supabaseCommands, { onConflict: 'command_id' });
if (error) {
throw error;
}
this.syncStatus.lastSyncTime = new Date();
this.syncStatus.pendingCommands = 0;
logger_1.logger.info('Commands synced successfully');
}
catch (error) {
logger_1.logger.error('Failed to sync commands', error);
this.syncStatus.syncErrors++;
throw error;
}
}
async syncSession(sessionInfo) {
if (!this.client)
return;
try {
const { error } = await this.client
.from('terminal_sessions')
.upsert({
session_id: sessionInfo.id,
start_time: sessionInfo.startTime,
end_time: sessionInfo.endTime,
user_name: sessionInfo.user,
hostname: sessionInfo.hostname,
shell: sessionInfo.shell,
terminal: sessionInfo.terminal
}, { onConflict: 'session_id' });
if (error) {
throw error;
}
logger_1.logger.info('Session synced successfully');
}
catch (error) {
logger_1.logger.error('Failed to sync session', error);
this.syncStatus.syncErrors++;
}
}
startPeriodicSync(intervalMinutes, getUnsyncedCommands) {
if (!this.client) {
logger_1.logger.warn('Cannot start periodic sync - Supabase not configured');
return;
}
const cronExpression = `*/${intervalMinutes} * * * *`;
this.syncTask = cron.schedule(cronExpression, async () => {
try {
const commands = await getUnsyncedCommands();
this.syncStatus.pendingCommands = commands.length;
if (commands.length > 0) {
await this.syncCommands(commands);
}
}
catch (error) {
logger_1.logger.error('Periodic sync failed', error);
this.syncStatus.syncErrors++;
}
});
this.syncTask.start();
logger_1.logger.info(`Started periodic sync every ${intervalMinutes} minutes`);
}
stopPeriodicSync() {
if (this.syncTask) {
this.syncTask.stop();
logger_1.logger.info('Stopped periodic sync');
}
}
async queryCommands(filters) {
if (!this.client) {
throw new Error('Supabase not configured');
}
try {
let query = this.client
.from('terminal_commands')
.select('*')
.order('timestamp', { ascending: false });
if (filters.startTime) {
query = query.gte('timestamp', filters.startTime.toISOString());
}
if (filters.endTime) {
query = query.lte('timestamp', filters.endTime.toISOString());
}
if (filters.searchTerm) {
query = query.ilike('command', `%${filters.searchTerm}%`);
}
if (filters.isSwarmPrompt !== undefined) {
query = query.eq('metadata->isSwarmPrompt', filters.isSwarmPrompt);
}
const { data, error } = await query;
if (error) {
throw error;
}
// Transform back to Command type
return (data || []).map(row => ({
id: row.command_id,
timestamp: new Date(row.timestamp),
command: row.command,
directory: row.directory,
user: row.user_name,
sessionId: row.session_id,
hostname: row.hostname,
exitCode: row.exit_code,
duration: row.duration,
metadata: row.metadata
}));
}
catch (error) {
logger_1.logger.error('Failed to query commands', error);
throw error;
}
}
getSyncStatus() {
return { ...this.syncStatus };
}
}
exports.SupabaseSync = SupabaseSync;
;