osc-mcp-server
Version:
Model Context Protocol server for OSC (Open Sound Control) endpoint management
227 lines • 8.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OSCManager = void 0;
exports.createOSCManager = createOSCManager;
const events_1 = require("events");
const endpoint_1 = require("./endpoint");
const index_1 = require("../errors/index");
class OSCManager extends events_1.EventEmitter {
endpoints = new Map();
nextEndpointId = 1;
async createEndpoint(config) {
try {
const existingEndpoint = this.findEndpointByPort(config.port);
if (existingEndpoint && existingEndpoint.isActive()) {
const error = index_1.NetworkErrors.portInUse(config.port, this.getSuggestedPorts(config.port));
return {
endpointId: '',
port: config.port,
status: 'error',
message: error.message,
};
}
const endpointId = this.generateEndpointId();
const endpoint = (0, endpoint_1.createOSCEndpoint)(endpointId, config);
this.setupEndpointEventHandlers(endpoint);
await endpoint.startListening();
this.endpoints.set(endpointId, endpoint);
const endpointInfo = endpoint.getStatus();
this.emit('endpointCreated', endpointInfo);
return {
endpointId,
port: config.port,
status: 'active',
message: `OSC endpoint created successfully on port ${config.port}`,
};
}
catch (error) {
if (error instanceof Error) {
if (error.message.includes('EADDRINUSE')) {
const networkError = index_1.NetworkErrors.portInUse(config.port, this.getSuggestedPorts(config.port));
return {
endpointId: '',
port: config.port,
status: 'error',
message: networkError.message,
};
}
else if (error.message.includes('EACCES')) {
const networkError = index_1.NetworkErrors.permissionDenied(config.port);
return {
endpointId: '',
port: config.port,
status: 'error',
message: networkError.message,
};
}
else if (error.message.includes('Invalid port')) {
const networkError = index_1.NetworkErrors.portInvalid(config.port);
return {
endpointId: '',
port: config.port,
status: 'error',
message: networkError.message,
};
}
}
const operationError = index_1.OperationErrors.operationFailed('createEndpoint', error instanceof Error ? error.message : 'Unknown error');
return {
endpointId: '',
port: config.port,
status: 'error',
message: operationError.message,
};
}
}
async stopEndpoint(endpointId) {
const endpoint = this.endpoints.get(endpointId);
if (!endpoint) {
const error = index_1.EndpointErrors.notFound(endpointId);
return {
endpointId,
message: error.message,
};
}
try {
if (!endpoint.isActive()) {
const error = index_1.EndpointErrors.alreadyStopped(endpointId);
return {
endpointId,
message: error.message,
};
}
await endpoint.stopListening();
this.endpoints.delete(endpointId);
this.emit('endpointStopped', endpointId);
return {
endpointId,
message: `Endpoint ${endpointId} stopped successfully`,
};
}
catch (error) {
const operationError = index_1.OperationErrors.operationFailed('stopEndpoint', error instanceof Error ? error.message : 'Unknown error');
return {
endpointId,
message: operationError.message,
};
}
}
getEndpointStatus(endpointId) {
if (endpointId) {
const endpoint = this.endpoints.get(endpointId);
if (!endpoint) {
return { endpoints: [] };
}
return { endpoints: [endpoint.getStatus()] };
}
const endpoints = Array.from(this.endpoints.values()).map(endpoint => endpoint.getStatus());
return { endpoints };
}
getMessages(endpointId, query = {}) {
let allMessages = [];
let totalCount = 0;
if (endpointId) {
const endpoint = this.endpoints.get(endpointId);
if (endpoint) {
const buffer = endpoint.getMessageBuffer();
allMessages = buffer.getMessages(query);
totalCount = buffer.getMessageCount();
}
}
else {
for (const endpoint of this.endpoints.values()) {
const buffer = endpoint.getMessageBuffer();
const endpointMessages = buffer.getMessages(query);
allMessages.push(...endpointMessages);
totalCount += buffer.getMessageCount();
}
allMessages.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
if (query.limit !== undefined && query.limit > 0) {
allMessages = allMessages.slice(0, query.limit);
}
}
return {
messages: allMessages,
totalCount,
filteredCount: allMessages.length,
};
}
getRecentMessages(timeWindowSeconds, endpointId, limit) {
const since = new Date(Date.now() - timeWindowSeconds * 1000);
const query = { since };
if (limit !== undefined) {
query.limit = limit;
}
const response = this.getMessages(endpointId, query);
return response.messages;
}
async shutdown() {
const stopPromises = [];
for (const [endpointId, endpoint] of this.endpoints) {
stopPromises.push(endpoint.stopListening().catch(error => {
console.error(`Error stopping endpoint ${endpointId}:`, error);
}));
}
await Promise.all(stopPromises);
this.endpoints.clear();
}
getActiveEndpointCount() {
return Array.from(this.endpoints.values()).filter(endpoint => endpoint.isActive()).length;
}
getTotalEndpointCount() {
return this.endpoints.size;
}
isPortInUse(port) {
const endpoint = this.findEndpointByPort(port);
return endpoint !== null && endpoint.isActive();
}
getUsedPorts() {
return Array.from(this.endpoints.values())
.filter(endpoint => endpoint.isActive())
.map(endpoint => endpoint.getPort());
}
findEndpointByPort(port) {
for (const endpoint of this.endpoints.values()) {
if (endpoint.getPort() === port) {
return endpoint;
}
}
return null;
}
generateEndpointId() {
const id = `endpoint-${this.nextEndpointId}`;
this.nextEndpointId++;
return id;
}
getSuggestedPorts(requestedPort) {
const suggestions = [];
const usedPorts = this.getUsedPorts();
let candidate = requestedPort + 1;
while (suggestions.length < 3 && candidate <= 65535) {
if (!usedPorts.includes(candidate)) {
suggestions.push(candidate);
}
candidate++;
}
return suggestions;
}
setupEndpointEventHandlers(endpoint) {
const endpointId = endpoint.getId();
endpoint.on('message', (message) => {
this.emit('messageReceived', endpointId, message);
});
endpoint.on('error', (error) => {
this.emit('endpointError', endpointId, error);
});
endpoint.on('statusChange', status => {
if (status === 'error') {
console.warn(`Endpoint ${endpointId} encountered an error and changed status to: ${status}`);
}
});
}
}
exports.OSCManager = OSCManager;
function createOSCManager() {
return new OSCManager();
}
//# sourceMappingURL=manager.js.map