hataraku
Version:
An autonomous coding agent for building AI-powered development tools. The name "Hataraku" (働く) means "to work" in Japanese.
103 lines • 3.89 kB
JavaScript
import { Thread } from './thread';
import fs from 'fs/promises';
import { existsSync } from 'fs';
/**
* A storage implementation that persists thread state to the file system.
* Implements the ThreadStorage interface for saving and loading thread state.
*/
export class FileSystemStorage {
filePath;
constructor(filePath) {
this.filePath = filePath;
}
/**
* Creates a new Thread with FileSystemStorage at the specified path.
*
* @param filePath - The file path where the thread state will be stored
* @returns A new Thread instance with FileSystemStorage
* @throws Error if a file already exists at the specified path
*/
static async create(filePath) {
// Check if file exists
if (existsSync(filePath)) {
throw new Error(`thread already exists: ${filePath}`);
}
return new Thread({ storage: new FileSystemStorage(filePath) });
}
/**
* Loads a Thread from the specified file path.
* Deserializes the thread state including special handling for Maps, Buffers, and Date objects.
*
* @param filePath - The file path from which to load the thread state
* @returns A Thread instance with the loaded state
* @throws Error if the file cannot be read or contains invalid JSON
*/
static async load(filePath) {
const data = await fs.readFile(filePath, 'utf8');
const state = JSON.parse(data, (key, value) => {
if (Array.isArray(value) && value.every(item => Array.isArray(item) && item.length === 2)) {
return new Map(value);
}
if (value && typeof value === 'object') {
if (value.type === 'Buffer') {
return Buffer.from(value.data, 'base64');
}
if (value.type === 'Date') {
return new Date(value.value);
}
}
return value;
});
return new Thread({
storage: new FileSystemStorage(filePath),
state
});
}
/**
* Saves the thread state to the file system.
* Creates any necessary directories and serializes special data types like Maps, Buffers, and Dates.
*
* @param state - The thread state to be saved
* @returns A promise that resolves when the save operation is complete
* @throws Error if the file cannot be written
*/
async save(state) {
const dirPath = this.filePath.split('/').slice(0, -1).join('/');
if (dirPath) {
await fs.mkdir(dirPath, { recursive: true });
}
const serializedState = JSON.stringify(state, (key, value) => {
if (value instanceof Map) {
return Array.from(value.entries());
}
if (value instanceof Buffer) {
return {
type: 'Buffer',
data: value.toString('base64')
};
}
if (value instanceof Date) {
return {
type: 'Date',
value: value.toISOString()
};
}
// Deep traverse objects to find and convert Date objects
if (value && typeof value === 'object' && !Array.isArray(value)) {
const converted = { ...value };
for (const [k, v] of Object.entries(value)) {
if (v instanceof Date) {
converted[k] = {
type: 'Date',
value: v.toISOString()
};
}
}
return converted;
}
return value;
});
await fs.writeFile(this.filePath, serializedState, 'utf8');
}
}
//# sourceMappingURL=file-storage.js.map