UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

85 lines • 3.8 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 { normalizeKeyPrefix } from "../../vfs/s3/config.js"; import { RecordNamespaceStore } from "../mount/namespace/store.js"; import { S3RecordClient } from "../record/s3.js"; import { WorkspaceStateStore, } from "./base.js"; /** * WorkspaceStateStore hosting the sessions + metadata group on S3. * * Object layout under one bucket and prefix: * * - `{prefix}workspaces/{ws}.json` — metadata record * - `{prefix}{ws}/sessions/{session_id}.json` — session table * * Every record write is CAS-gated by a conditional PUT anchored on the * compare-read's ETag, giving S3 the same generation contract as the * Redis Lua script; bucket versioning (when enabled) doubles as an * audit trail for free. This store hosts only the sessions+meta * group: namespace nodes and observer events are chatty per-op planes * that belong on RAM or Redis, so use this store as the `workspace` * group override of a RAM or Redis default store. Mirrors the Python * S3WorkspaceStateStore. */ export class S3WorkspaceStateStore extends WorkspaceStateStore { config; prefix; meta; sessionTables = new Map(); constructor(config, overrides = {}) { super(overrides); this.config = config; this.prefix = normalizeKeyPrefix(config.keyPrefix) ?? ''; this.meta = new S3RecordClient(config, `${this.prefix}workspaces/`); } makeNamespace(_workspaceId) { throw new Error('The s3 store hosts only the sessions+meta group; keep the namespace ' + "plane on RAM or Redis and pass the s3 store as the 'workspace' group override."); } makeObserver(_workspaceId) { throw new Error('The s3 store hosts only the sessions+meta group; keep the observer ' + "plane on RAM or Redis and pass the s3 store as the 'workspace' group override."); } makeJobs(_workspaceId) { throw new Error('The s3 store hosts only the sessions+meta group; keep the jobs ' + "plane on RAM or Redis and pass the s3 store as the 'workspace' group override."); } makeSessions(workspaceId) { let table = this.sessionTables.get(workspaceId); if (table === undefined) { const scoped = normalizeKeyPrefix(`${this.prefix}${workspaceId}/`) ?? ''; table = new RecordNamespaceStore(new S3RecordClient(this.config, `${scoped}sessions/`)); this.sessionTables.set(workspaceId, table); } return table; } async readMeta(workspaceId) { const [fields] = await this.meta.get(workspaceId); return fields; } casWriteMeta(workspaceId, fields, expectedGeneration) { return this.meta.casPut(workspaceId, fields, expectedGeneration); } async forgetSelf(workspaceId) { await this.sessionTables.get(workspaceId)?.close(); this.sessionTables.delete(workspaceId); await this.meta.delete([workspaceId]); } async closeSelf() { for (const table of this.sessionTables.values()) await table.close(); await this.meta.close(); } } //# sourceMappingURL=s3.js.map