UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

116 lines • 4.29 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { RAMConsoleStore } from "../console/index.js"; import { generationOf } from "../../workspace/record/types.js"; import { JobStore } from "./store.js"; /** The consoles a job list names, its disowned jobs' included. */ function consoleNames(fields) { const jobs = (fields?.jobs ?? {}); const disowned = (fields?.disowned ?? {}); return new Set([...Object.values(jobs).map((job) => job.console), ...Object.keys(disowned)]); } /** * JobStore held in process memory (the default). * * Workspaces that share one provider in a process share their jobs through * it. A console is dropped once no list names it; a reader already holding * it keeps reading, as a console outlives its entry. Mirrors Python's * `RAMJobStore`. */ export class RAMJobStore extends JobStore { lists = new Map(); consoles = new Map(); leases = new Map(); published = new Map(); stops = new Map(); load(sessionId) { const fields = this.lists.get(sessionId); return Promise.resolve(fields === undefined ? null : structuredClone(fields)); } casSet(sessionId, fields, expectedGeneration) { const stored = this.lists.get(sessionId); if (generationOf(stored) !== expectedGeneration) return Promise.resolve(false); this.lists.set(sessionId, structuredClone(fields)); const kept = consoleNames(fields); this.drop([...consoleNames(stored)].filter((name) => !kept.has(name))); return Promise.resolve(true); } delete(sessionId, expectedGeneration) { const stored = this.lists.get(sessionId); if (generationOf(stored) !== expectedGeneration) return Promise.resolve(false); this.lists.delete(sessionId); this.drop([...consoleNames(stored)]); return Promise.resolve(true); } console(name) { let store = this.consoles.get(name); if (store === undefined) { store = new RAMConsoleStore(); this.consoles.set(name, store); } return store; } renew(owner, _consoles = []) { if (this.leaseSeconds !== null) { this.leases.set(owner, performance.now() + this.leaseSeconds * 1000); } return Promise.resolve(); } alive(owner) { if (this.leaseSeconds === null) return Promise.resolve(true); return Promise.resolve((this.leases.get(owner) ?? 0) > performance.now()); } publish(owner, processes) { if (Object.keys(processes).length > 0) this.published.set(owner, structuredClone(processes)); else { this.published.delete(owner); this.stops.delete(owner); } return Promise.resolve(); } processes() { return Promise.resolve(structuredClone(Object.fromEntries(this.published))); } requestStop(owner, pid, signal) { const asked = this.stops.get(owner) ?? []; asked.push([pid, signal]); this.stops.set(owner, asked); return Promise.resolve(); } takeStops(owner) { const asked = this.stops.get(owner) ?? []; this.stops.delete(owner); return Promise.resolve(asked); } clear() { this.lists.clear(); this.consoles.clear(); this.leases.clear(); this.published.clear(); this.stops.clear(); return Promise.resolve(); } close() { return Promise.resolve(); } drop(names) { for (const name of names) this.consoles.delete(name); } } //# sourceMappingURL=ram.js.map