@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
108 lines • 4.67 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 { PathSpec } from "../../types.js";
import { LanguageRuntime } from "../language.js";
import { PAYLOAD_ARGV0, STDIN_ARGV0 } from "./execution.js";
/** CPython's own four answers, pinned on 3.12.13/3.13.7. */
export const CPYTHON_ARGV0 = {
payload: PAYLOAD_ARGV0,
stdinOperand: STDIN_ARGV0,
bareStdin: '',
namesFile: true,
};
/**
* The program that `-m mod` runs, on any engine that is real CPython.
*
* runpy does the work: run_module finds the module, runs it under
* __main__, and alter_sys rewrites sys.argv[0] to the module's own
* file, which is what CPython puts there. Engines that are not CPython
* declare runsModules false and never see this.
*
* The existence probe is not redundant with run_module: CPython answers
* a missing module with one line and exit 1, while bare run_module
* raises, which would reach the user as a runpy traceback. Probing
* first also keeps an ImportError raised INSIDE the module distinct
* from the module itself being absent, which a try around run_module
* could not tell apart.
*
* @param name the module to run.
* @param label the command name used in the not-found message.
*/
export function moduleSource(name, label) {
return [
'import importlib.util, runpy, sys',
`_name = ${JSON.stringify(name)}`,
`_label = ${JSON.stringify(label)}`,
'try:',
' _found = importlib.util.find_spec(_name) is not None',
// find_spec raises rather than returning None when an ancestor of a
// dotted name is missing or is not a package; either way the module
// cannot be found, and the message below reports it.
'except (ImportError, TypeError, ValueError):',
' _found = False',
'if not _found:',
" sys.stderr.write(_label + ': No module named ' + _name + chr(10))",
' raise SystemExit(1)',
"runpy.run_module(_name, run_name='__main__', alter_sys=True)",
'',
].join('\n');
}
/**
* The python tier: every runtime that interprets Python source.
*
* Groups the engines behind the python3/python commands (pyodide,
* monty here; wasi, local in Python and @struktoai/mirage-node), so
* `language` and the default captures are declared once and
* python-tier behavior has one home. A new Python engine subclasses
* this, not LanguageRuntime.
*/
export class PythonRuntime extends LanguageRuntime {
language = 'python';
/** The tier's head words: the class-default captures of every python engine. */
static commands = ['python3', 'python'];
argv0Rules = CPYTHON_ARGV0;
/**
* Whether `python3 -m mod` can run at all. True for every engine that
* is real CPython, since runpy does the work; false for an engine
* implementing a subset of the language with no import system to find
* a module with. A capability of the tier, declared here rather than
* probed, so a refusal can name the runtime.
*/
runsModules = true;
versionSuffix = '';
moduleCode(name, label) {
return this.runsModules ? moduleSource(name, label) : null;
}
async version(env, signal, timeoutSeconds) {
// Process runtimes must supply a probe that cannot run startup hooks.
if (this.reach !== 'workspace')
return super.version(env, signal, timeoutSeconds);
return this.interpret(await this.engine(), {
code: `import sys\nprint('Python ' + sys.version.split()[0] + ${JSON.stringify(this.versionSuffix)})`,
args: [],
stdin: null,
}, {
stdin: null,
flags: {},
cwd: PathSpec.fromStrPath('/'),
env,
...(signal !== undefined ? { signal } : {}),
...(timeoutSeconds !== undefined ? { timeoutSeconds } : {}),
});
}
constructor(options = {}, configKeys = []) {
super(options, PythonRuntime.commands, configKeys);
}
}
//# sourceMappingURL=base.js.map