@commit451/salamander
Version:
Never be AFK
128 lines • 4.83 kB
JavaScript
import { collection, doc, getDocs, onSnapshot, orderBy, query, where, } from 'firebase/firestore';
import { db } from '../config/firebase.js';
import { AuthService } from './auth.js';
import { ApiService } from './api.js';
import { RunnerType } from '../types/runner.js';
import { generateDeviceId, getDeviceDisplayName } from '../utils/device.js';
import { EncryptionService } from '../utils/encryption.js';
import { loadAuth, saveAuth } from '../utils/storage.js';
export class RunnerService {
static RUNNERS_COLLECTION = 'runners';
static async getAllRunners() {
const userId = AuthService.userId;
const deviceId = generateDeviceId();
if (!userId) {
throw new Error('User not authenticated');
}
const q = query(collection(db, this.RUNNERS_COLLECTION), where('userId', '==', userId), where('machineId', "==", deviceId), orderBy('lastUsed', 'desc'));
const querySnapshot = await getDocs(q);
return querySnapshot.docs.map(doc => this.docToRunner(doc.data(), doc.id));
}
static async createRunner(data) {
const userId = AuthService.userId;
if (!userId) {
throw new Error('User not authenticated');
}
const deviceId = generateDeviceId();
const deviceName = getDeviceDisplayName();
// Generate or load encryption code
let encryptionCode;
const storedAuth = await loadAuth();
if (storedAuth?.encryptionCode) {
encryptionCode = storedAuth.encryptionCode;
}
else {
// Generate new random encryption code
encryptionCode = EncryptionService.generateRandomSecret();
// Save to storage
const updatedAuth = { ...storedAuth, encryptionCode };
await saveAuth(updatedAuth);
}
// Create verification string
const encryptionVerification = EncryptionService.createVerificationString(encryptionCode);
// Create runner via API with encryption verification
const response = await ApiService.createRunner({
name: data.name,
directory: data.directory,
machineId: deviceId,
machineName: deviceName,
encryptionVerification
});
const runnerId = response.id;
return {
id: runnerId,
name: data.name,
directory: data.directory,
userId,
runnerType: data.runnerType,
createdAt: new Date(),
lastUsed: new Date(),
machineId: deviceId,
machineName: deviceName,
};
}
static async deleteRunner(runnerId) {
// Call the API to delete the runner
await ApiService.deleteRunner(runnerId);
}
static async clearPendingCommand(runnerId) {
await ApiService.updateRunner(runnerId, {
clearPendingCommand: true
});
}
static async updateRunnerAfterCommand(runnerId, result) {
await ApiService.updateRunner(runnerId, {
updateLastUsed: true,
lastMessage: result ?? ''
});
}
static async createMessage(runnerId, data) {
const userId = AuthService.userId;
if (!userId) {
throw new Error('User not authenticated');
}
if (!runnerId) {
throw new Error('Runner ID is required to create a message');
}
await ApiService.createRunnerMessage(runnerId, data);
}
static listenToRunner(runnerId, callback) {
const runnerDoc = doc(db, this.RUNNERS_COLLECTION, runnerId);
return onSnapshot(runnerDoc, (doc) => {
if (doc.exists()) {
const runner = this.docToRunner(doc.data(), doc.id);
callback(runner);
}
else {
callback(null);
}
});
}
static docToRunner(data, id) {
return {
id,
name: data.name || '',
directory: data.directory || '',
userId: data.userId || '',
runnerType: this.parseRunnerType(data.runnerType),
createdAt: data.createdAt?.toDate() || null,
lastUsed: data.lastUsed?.toDate() || null,
lastMessage: data.lastMessage || undefined,
machineName: data.machineName || undefined,
machineId: data.machineId || undefined,
pendingCommand: data.pendingCommand || undefined,
};
}
static parseRunnerType(type) {
switch (type) {
case 'codex':
return RunnerType.CODEX;
case 'gemini':
return RunnerType.GEMINI;
case 'claude':
default:
return RunnerType.CLAUDE;
}
}
}
//# sourceMappingURL=runner.js.map