-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshared-storage.js
More file actions
184 lines (156 loc) · 6.04 KB
/
Copy pathshared-storage.js
File metadata and controls
184 lines (156 loc) · 6.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env node
/**
* Shared storage for OSC messages between MCP server and dashboard
* Uses file-based storage for inter-process communication
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Configuration for shared storage
const STORAGE_CONFIG = {
MAX_MESSAGES: parseInt(process.env.MAX_OSC_MESSAGES || '1000'),
LOG_ROTATION: process.env.OSC_LOG_ROTATION === 'true' || process.env.OSC_LOG_ROTATION === 'daily',
LOGS_DIR: path.join(__dirname, 'logs')
};
// Get storage file name with date rotation support
function getStorageFileName() {
if (STORAGE_CONFIG.LOG_ROTATION) {
const today = new Date();
const dateStr = today.getFullYear() + '-' +
String(today.getMonth() + 1).padStart(2, '0') + '-' +
String(today.getDate()).padStart(2, '0');
return path.join(STORAGE_CONFIG.LOGS_DIR, `osc-messages-${dateStr}.json`);
}
return path.join(STORAGE_CONFIG.LOGS_DIR, 'osc-messages.json');
}
const STORAGE_FILE = getStorageFileName();
// Import command queue for auto-queuing
let commandQueue = null;
try {
const { commandQueue: cq } = await import('./osc-command-queue.js');
commandQueue = cq;
} catch (error) {
console.warn('[SHARED STORAGE] Command queue not available:', error.message);
}
// Ensure logs directory exists
function ensureLogsDir() {
const logsDir = path.dirname(STORAGE_FILE);
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
}
// Load messages from file with retry logic
function loadMessages() {
let retries = 3;
while (retries > 0) {
try {
ensureLogsDir();
if (fs.existsSync(STORAGE_FILE)) {
const data = fs.readFileSync(STORAGE_FILE, 'utf8');
if (data.trim()) {
const messages = JSON.parse(data);
console.log(`[SHARED STORAGE] Successfully loaded ${messages.length} messages from ${STORAGE_FILE}`);
return messages;
}
}
console.log(`[SHARED STORAGE] File not found or empty: ${STORAGE_FILE}`);
return [];
} catch (error) {
retries--;
console.warn(`[SHARED STORAGE] Failed to load messages (${retries} retries left):`, error.message);
if (retries === 0) {
console.error(`[SHARED STORAGE] All retries exhausted for loading: ${STORAGE_FILE}`);
return [];
}
// Wait 100ms before retry
const start = Date.now();
while (Date.now() - start < 100) {
// Busy wait
}
}
}
return [];
}
// Save messages to file
function saveMessages(messages) {
try {
ensureLogsDir();
// Atomic write: write to temp file first, then rename
const tempFile = STORAGE_FILE + '.tmp';
const data = JSON.stringify(messages, null, 2);
fs.writeFileSync(tempFile, data);
fs.renameSync(tempFile, STORAGE_FILE);
console.log(`[SHARED STORAGE] Successfully saved ${messages.length} messages to ${STORAGE_FILE}`);
} catch (error) {
console.error('[SHARED STORAGE] Failed to save OSC messages:', error.message);
console.error('[SHARED STORAGE] Storage file path:', STORAGE_FILE);
}
}
export function addOSCMessage(address, args, source, port, direction = 'inbound') {
const message = {
address,
args,
source,
port,
timestamp: new Date().toISOString(),
direction // 'inbound' from MaxMSP, 'outbound' to MaxMSP
};
// Load existing messages
const messages = loadMessages();
messages.push(message);
// Keep only last N messages (configurable)
if (messages.length > STORAGE_CONFIG.MAX_MESSAGES) {
messages.splice(0, messages.length - STORAGE_CONFIG.MAX_MESSAGES);
}
// Save back to file
saveMessages(messages);
// Auto-queue certain inbound messages for Claude processing
if (direction === 'inbound' && commandQueue && shouldAutoQueue(address)) {
try {
const intent = extractIntent(address, args);
commandQueue.addCommand(address, args, source, port, intent);
console.log(`[SHARED STORAGE] Auto-queued for Claude: ${address}`);
} catch (error) {
console.warn('[SHARED STORAGE] Failed to auto-queue command:', error.message);
}
}
const currentFile = path.basename(getStorageFileName());
console.log(`[SHARED STORAGE FILE] OSC stored: ${address} ${direction} ${source}:${port} (total: ${messages.length})`);
console.log(`[SHARED STORAGE FILE] File: ${currentFile}, Max: ${STORAGE_CONFIG.MAX_MESSAGES}, Rotation: ${STORAGE_CONFIG.LOG_ROTATION ? 'Daily' : 'Single'}`);
return message;
}
// Helper function to determine if message should be auto-queued
function shouldAutoQueue(address) {
const autoQueuePatterns = [
'/claude/',
'/query/',
'/request/',
'/ask/',
'/command/',
'/ai/',
'/process/'
];
return autoQueuePatterns.some(pattern => address.toLowerCase().includes(pattern));
}
// Helper function to extract intent from OSC message
function extractIntent(address, args) {
// Extract meaningful intent from address and args
const addressParts = address.split('/').filter(part => part.length > 0);
const lastPart = addressParts[addressParts.length - 1];
if (args && args.length > 0) {
return `${lastPart} with ${args.length} argument(s): ${args.slice(0, 3).join(', ')}${args.length > 3 ? '...' : ''}`;
}
return lastPart || 'process command';
}
export function getOSCMessages(limit = 100) {
const messages = loadMessages();
return messages.slice(-limit);
}
export function getOSCMessageCount() {
const messages = loadMessages();
return messages.length;
}
export function clearOSCMessages() {
saveMessages([]);
}