@dailyautomations/terminal-logger
Version:
Terminal command logger with Supabase sync for swarm prompts
206 lines (205 loc) • 7.62 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.CommandCapture = void 0;
const fs = __importStar(require("fs"));
const readline = __importStar(require("readline"));
const logger_1 = require("./logger");
const uuid_1 = require("uuid");
const os = __importStar(require("os"));
const child_process_1 = require("child_process");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class CommandCapture {
historyFile;
sessionId;
lastPosition = 0;
watchInterval;
constructor(historyFile, sessionId) {
// Expand ~ to home directory
this.historyFile = historyFile.replace(/^~/, os.homedir());
this.sessionId = sessionId || (0, uuid_1.v4)();
}
async startWatching(onCommand) {
logger_1.logger.info('Starting command capture', { historyFile: this.historyFile });
// Initial read to get current position
await this.initializePosition();
// Use fs.watch for better performance instead of polling
try {
fs.watch(this.historyFile, async (eventType) => {
if (eventType === 'change') {
try {
await this.checkForNewCommands(onCommand);
}
catch (error) {
logger_1.logger.error('Error checking for new commands', error);
}
}
});
logger_1.logger.info('Started file watcher on history file');
}
catch (error) {
logger_1.logger.warn('Failed to set up file watcher, falling back to polling', error);
// Fallback to polling if fs.watch fails
this.watchInterval = setInterval(async () => {
try {
await this.checkForNewCommands(onCommand);
}
catch (error) {
logger_1.logger.error('Error checking for new commands', error);
}
}, 1000);
}
// Also listen to process stdin if available
this.setupStdinCapture(onCommand);
}
stopWatching() {
if (this.watchInterval) {
clearInterval(this.watchInterval);
logger_1.logger.info('Stopped command capture');
}
}
async initializePosition() {
try {
const stats = await fs.promises.stat(this.historyFile);
this.lastPosition = stats.size;
logger_1.logger.info('History file found', {
path: this.historyFile,
size: stats.size
});
}
catch (error) {
logger_1.logger.warn('History file not found, starting from beginning', {
path: this.historyFile,
error: error.message
});
this.lastPosition = 0;
}
}
async checkForNewCommands(onCommand) {
try {
const stats = await fs.promises.stat(this.historyFile);
if (stats.size > this.lastPosition) {
const stream = fs.createReadStream(this.historyFile, {
start: this.lastPosition,
encoding: 'utf-8'
});
const rl = readline.createInterface({
input: stream,
crlfDelay: Infinity
});
for await (const line of rl) {
if (line.trim()) {
const command = await this.parseCommand(line);
onCommand(command);
}
}
this.lastPosition = stats.size;
}
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
}
async parseCommand(line) {
const timestamp = new Date();
const cwd = process.cwd();
const user = os.userInfo().username;
const hostname = os.hostname();
// Detect if this is a swarm-related command
const isSwarmPrompt = this.isSwarmCommand(line);
const metadata = isSwarmPrompt ? this.extractSwarmMetadata(line) : undefined;
return {
id: (0, uuid_1.v4)(),
timestamp,
command: line,
directory: cwd,
user,
sessionId: this.sessionId,
hostname,
metadata
};
}
isSwarmCommand(command) {
const swarmPatterns = [
/claude-flow\s+swarm/i,
/npx\s+claude-flow/i,
/swarm\s+(init|spawn|orchestrate)/i,
/mcp__.*__/i
];
return swarmPatterns.some(pattern => pattern.test(command));
}
extractSwarmMetadata(command) {
const metadata = { isSwarmPrompt: true };
// Extract swarm ID if present
const swarmIdMatch = command.match(/swarm[_-]?id[:\s]+([a-zA-Z0-9_-]+)/i);
if (swarmIdMatch) {
metadata.swarmId = swarmIdMatch[1];
}
// Extract agent type
const agentMatch = command.match(/agent[_-]?type[:\s]+(\w+)/i);
if (agentMatch) {
metadata.agentType = agentMatch[1];
}
// Extract task ID
const taskMatch = command.match(/task[_-]?id[:\s]+([a-zA-Z0-9_-]+)/i);
if (taskMatch) {
metadata.taskId = taskMatch[1];
}
return metadata;
}
setupStdinCapture(onCommand) {
// This is a more advanced feature that would hook into the shell
// For now, we'll just log that it's not implemented
logger_1.logger.debug('Direct stdin capture not implemented, relying on history file');
}
async captureCurrentCommand() {
try {
// Try to get the last command from history
const { stdout } = await execAsync('tail -n 1 ~/.bash_history');
const lastCommand = stdout.trim();
if (lastCommand) {
return this.parseCommand(lastCommand);
}
}
catch (error) {
logger_1.logger.error('Failed to capture current command', error);
}
return null;
}
}
exports.CommandCapture = CommandCapture;
;