@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
116 lines • 4.29 kB
JavaScript
// ========= 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