@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
128 lines • 5.27 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 { currentCaller } from "../context/session_context.js";
import { OutputStream } from "./stream.js";
import { createAsyncContext } from "../utils/async_context.js";
import { rstripSlash } from "../utils/slash.js";
import { runWithNetwork } from "./network.js";
/**
* What judges the connections a line or a file op opens: the workspace's
* `network` (null for no limit) and its policies, whose preNetwork and
* postNetwork each connection answers to. `hooked` settles whether one of
* them listens for the session's connections, so each goes through the
* gateway, and `listening` holds the answer once it has: the binding starts
* the line or the op at once, as a browser's storage must keep its frames in
* step, and a connection opened first waits for it. `session` is the
* session it runs for (null when unknown), and `op` whether a file op, not a
* line, bound it. Mirrors Python's `Egress`.
*/
export class Egress {
rule;
policies;
session;
op;
listening = null;
hooked;
constructor(rule, policies, hooked, session = null, op = false) {
this.rule = rule;
this.policies = policies;
this.session = session;
this.op = op;
this.hooked = hooked.then((answer) => (this.listening = answer));
}
}
const egress = createAsyncContext();
async function listens(policies) {
if (policies === null)
return false;
const sessionId = currentCaller().session?.sessionId ?? '';
for (const method of ['preNetwork', 'postNetwork']) {
if (policies.wants(method) && (await policies.wantsFor(method, sessionId)))
return true;
}
return false;
}
/**
* `fn` run as a line or a file op of a workspace: every connection it opens
* clears `rule`, and passes the gateway when a policy listens for the
* session's connections; for `session`, null when unknown, and `op` when
* a file op, not a line, binds it. Mirrors
* Python's `run_with_egress`.
*/
export function runWithEgress(rule, policies, fn, session = null, op = false) {
const outer = egress.getStore();
const hooked = outer?.policies === policies ? outer.hooked : listens(policies);
return runWithNetwork(rule, () => Promise.resolve(egress.run(new Egress(rule, policies, hooked, session, op), fn)));
}
/**
* `it` pulled and closed under the egress bound where it was made: a mount's
* stream opens each later connection, whoever pulls it and however late,
* under the workspace's rule and policies, as `withCaller` keeps its caller.
* Its close reaches the stream even before a first pull. Handed back as it
* is where neither limits anything. `owner` names the egress outright where
* a browser's storage, which answers its newest frame, could hand another
* workspace's. Mirrors Python's `with_egress`.
*/
export function withEgress(it, owner = egress.getStore() ?? null) {
const bound = owner;
if (bound === null ||
(bound.rule === null && (bound.policies === null || bound.listening === false))) {
return it;
}
const under = (fn) => runWithNetwork(bound.rule, () => Promise.resolve(egress.run(bound, fn)));
const iterator = it[Symbol.asyncIterator]();
async function* pulled() {
for (;;) {
const step = await under(() => iterator.next());
if (step.done === true)
return;
yield step.value;
}
}
return new OutputStream(pulled(), async () => {
await under(async () => {
await iterator.return?.();
});
});
}
/**
* The egress of every call live in this context: on an isolating runtime the
* bound one alone, in a browser every concurrently bound one, since it cannot
* tell whose call is opening the connection. Mirrors Python's `live_egress`.
*/
export function liveEgress() {
return egress.liveStores();
}
/**
* The context a connection to `address` on `port`, opened for `host`,
* reports: the command, the mount and the session of the call opening it.
* Mirrors Python's `_opened`.
*/
export function opened(host, address, port) {
const caller = currentCaller();
const invocation = caller.invocation;
const mount = caller.vfsMount ?? '';
return {
host,
address,
port,
command: caller.command ?? '',
program: invocation?.program ?? [],
words: invocation?.words ?? [],
line: invocation?.line ?? '',
mount: rstripSlash(mount) || mount,
sessionId: caller.session?.sessionId ?? '',
};
}
//# sourceMappingURL=gateway.js.map