consortium
Version:
Remote control and session sharing CLI for AI coding agents
107 lines (92 loc) • 3.16 kB
JavaScript
const fs = require('fs');
// Disable autoupdater (never works really)
process.env.DISABLE_AUTOUPDATER = '1';
// Where the fetch-activity JSONL goes.
//
// Normally fd 3, a pipe the parent opened. When Claude runs inside a PTY
// (so the session can be mirrored to the Consortium app) there is no spare
// fd, and the parent passes a socket path instead. Events are advisory —
// they drive a thinking indicator — so anything that cannot be delivered is
// dropped rather than buffered without limit.
const eventsSocketPath = process.env.CONSORTIUM_FETCH_EVENTS_SOCKET;
let eventsSocket = null;
const pendingEvents = [];
if (eventsSocketPath) {
const net = require('net');
const socket = net.connect(eventsSocketPath);
socket.on('connect', () => {
eventsSocket = socket;
while (pendingEvents.length > 0) {
socket.write(pendingEvents.shift());
}
});
socket.on('error', () => { eventsSocket = null; });
socket.on('close', () => { eventsSocket = null; });
// Never hold the process open for a status channel.
if (socket.unref) socket.unref();
}
function writeMessage(message) {
const line = JSON.stringify(message) + '\n';
if (eventsSocketPath) {
if (eventsSocket) {
try { eventsSocket.write(line); } catch (err) { /* channel gone */ }
} else if (pendingEvents.length < 64) {
pendingEvents.push(line);
}
return;
}
try {
fs.writeSync(3, line);
} catch (err) {
// fd 3 not available, ignore
}
}
// Intercept fetch to track thinking state
const originalFetch = global.fetch;
let fetchCounter = 0;
global.fetch = function(...args) {
const id = ++fetchCounter;
const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';
const method = args[1]?.method || 'GET';
// Parse URL for privacy
let hostname = '';
let path = '';
try {
const urlObj = new URL(url, 'http://localhost');
hostname = urlObj.hostname;
path = urlObj.pathname;
} catch (e) {
// If URL parsing fails, use defaults
hostname = 'unknown';
path = url;
}
// Send fetch start event
writeMessage({
type: 'fetch-start',
id,
hostname,
path,
method,
timestamp: Date.now()
});
// Execute the original fetch immediately
const fetchPromise = originalFetch(...args);
// Attach handlers to send fetch end event
const sendEnd = () => {
writeMessage({
type: 'fetch-end',
id,
timestamp: Date.now()
});
};
// Send end event on both success and failure
fetchPromise.then(sendEnd, sendEnd);
// Return the original promise unchanged
return fetchPromise;
};
// Preserve fetch properties
Object.defineProperty(global.fetch, 'name', { value: 'fetch' });
Object.defineProperty(global.fetch, 'length', { value: originalFetch.length });
// Import global Claude Code CLI
const { getClaudeCliPath, runClaudeCli } = require('./claude_version_utils.cjs');
runClaudeCli(getClaudeCliPath());