mlld
Version:
mlld: a modular prompt scripting language
1,340 lines (1,336 loc) • 340 kB
JavaScript
import { parseFrontmatter } from './chunk-UUW3GA6G.mjs';
import { accessField } from './chunk-27PI3WNS.mjs';
import { isVariable, extractVariableValue as extractVariableValue$1 } from './chunk-7ASO6AZA.mjs';
import { JSONFormatter } from './chunk-VFHGNRP2.mjs';
import { evaluateForeachSection, evaluateForeachCommand } from './chunk-ODBVL5OR.mjs';
import { createRenamedContentVariable, extractVariableValue, createLoadContentResultVariable } from './chunk-WSDJAWOM.mjs';
import { llmxmlInstance } from './chunk-GPFWQ7PB.mjs';
import { InterpolationContext, EscapingStrategyFactory } from './chunk-RZIZRJBS.mjs';
import { createPipelineInput } from './chunk-4IHDDJBM.mjs';
import { parse } from './chunk-VI5FKUWW.mjs';
import { version, checkMlldVersion, formatVersionError } from './chunk-LTNARAHB.mjs';
import { TaintLevel } from './chunk-KYJC7SAY.mjs';
import { logger, interpreterLogger } from './chunk-XGMRAGIT.mjs';
import { MlldDependencyError, MlldCommandExecutionError, MlldError, MlldImportError, MlldConditionError, MlldOutputError, MlldDirectiveError } from './chunk-YMCO2JI3.mjs';
import { isTextNode, astLocationToSourceLocation, isWhenSimpleNode, isWhenMatchNode, isWhenBlockNode, isPrimitiveValue, isDirectiveValue, isVariableReferenceValue, isTemplateValue, isExecInvocation, isLiteralNode } from './chunk-6BOVVHHZ.mjs';
import { isExecutableVariable, isTextLike, isArray, isObject, isCommandResult, isPipelineInput, isExecutable, isPath, isImported, isTemplate, isComputed, isPrimitive } from './chunk-V5L6FBQT.mjs';
import { createPathVariable, createSimpleTextVariable, createObjectVariable, createImportedVariable, createExecutableVariable, createArrayVariable, createCommandResultVariable, createComputedVariable, createFileContentVariable, createSectionContentVariable, createTemplateVariable, createInterpolatedTextVariable, createPipelineInputVariable } from './chunk-TU56GBG3.mjs';
import { isLoadContentResult, isLoadContentResultArray } from './chunk-444AWGDO.mjs';
import { __name, __publicField, __require } from './chunk-OMKLS24H.mjs';
import { execSync } from 'child_process';
import { AsyncLocalStorage } from 'async_hooks';
import * as crypto from 'crypto';
import * as path3 from 'path';
import { JSDOM } from 'jsdom';
import { glob } from 'tinyglobby';
import { Readability } from '@mozilla/readability';
import TurndownService from 'turndown';
// interpreter/utils/type-guard-helpers.ts
function getTextContent(node) {
return node && isTextNode(node) ? node.content : void 0;
}
__name(getTextContent, "getTextContent");
// interpreter/eval/path.ts
async function evaluatePath(directive, env) {
const identifierNodes = directive.values?.identifier;
if (!identifierNodes || !Array.isArray(identifierNodes) || identifierNodes.length === 0) {
throw new Error("Path directive missing identifier");
}
const identifierNode = identifierNodes[0];
let identifier;
if (identifierNode.type === "Text" && "content" in identifierNode) {
identifier = identifierNode.content;
} else if (identifierNode.type === "VariableReference" && "identifier" in identifierNode) {
identifier = identifierNode.identifier;
} else {
throw new Error("Path directive identifier must be a simple variable name");
}
const pathNodes = directive.values?.path;
if (!pathNodes) {
throw new Error("Path directive missing path");
}
const pathNode = pathNodes[0];
const isURL = pathNode?.subtype === "urlPath" || pathNode?.subtype === "urlSectionPath";
const interpolatedPath = await interpolate(pathNodes, env);
const security = directive.meta ? {
ttl: directive.meta.ttl,
trust: directive.meta.trust
} : void 0;
let resolvedPath = interpolatedPath;
const resolverManager = env.getResolverManager();
if (resolverManager && interpolatedPath.startsWith("@")) {
const pathParts = interpolatedPath.substring(1).split("/");
const potentialResolver = pathParts[0];
if (resolverManager.isResolverName(potentialResolver)) {
try {
const resolverContent = await env.resolveModule(interpolatedPath, "path");
if (resolverContent.contentType === "module") {
throw new Error(`Cannot use module as path: ${interpolatedPath} (modules must be imported, not used as paths)`);
}
resolvedPath = resolverContent.content;
} catch (error) {
if (error.message?.includes("Cannot use module as path")) {
throw error;
}
resolvedPath = await env.resolvePath(interpolatedPath);
}
} else {
resolvedPath = await env.resolvePath(interpolatedPath);
}
} else if (isURL || env.isURL(interpolatedPath)) {
resolvedPath = interpolatedPath;
} else {
if (interpolatedPath.startsWith("@PROJECTPATH") || interpolatedPath.startsWith("/")) {
resolvedPath = await env.resolvePath(interpolatedPath);
}
resolvedPath = resolvedPath.replace(/^\.\//, "");
}
const source = {
directive: "var",
syntax: "path",
hasInterpolation: false,
isMultiLine: false
};
const location = astLocationToSourceLocation(directive.location, env.getCurrentFilePath());
const variable = createPathVariable(identifier, resolvedPath, interpolatedPath, isURL || env.isURL(resolvedPath), resolvedPath.startsWith("/"), source, security, {
definedAt: location
});
env.setVariable(identifier, variable);
return {
value: resolvedPath,
env
};
}
__name(evaluatePath, "evaluatePath");
var _DefaultDependencyChecker = class _DefaultDependencyChecker {
constructor() {
__publicField(this, "cache", /* @__PURE__ */ new Map());
}
async checkNodePackages(packages) {
const missing = [];
const mismatched = [];
if (process.env.MLLD_TEST_MODE === "true") {
return {
satisfied: true,
missing: [],
mismatched: []
};
}
for (const [pkg, constraint] of Object.entries(packages)) {
const cacheKey = `node:${pkg}:${constraint}`;
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (!cached.satisfied) {
missing.push(...cached.missing);
mismatched.push(...cached.mismatched);
}
continue;
}
try {
let version2 = null;
try {
const localResult = execSync(`npm list ${pkg} --json --depth=0`, {
encoding: "utf8",
stdio: "pipe"
});
const data = JSON.parse(localResult);
version2 = data.dependencies?.[pkg]?.version;
} catch {
try {
const globalResult = execSync(`npm list -g ${pkg} --json --depth=0`, {
encoding: "utf8",
stdio: "pipe"
});
const data = JSON.parse(globalResult);
version2 = data.dependencies?.[pkg]?.version;
} catch {
}
}
if (!version2) {
missing.push(`${pkg}@${constraint}`);
} else if (!satisfiesConstraint(version2, constraint)) {
mismatched.push(`${pkg}@${version2} (needs ${constraint})`);
}
} catch (error) {
missing.push(`${pkg}@${constraint}`);
}
const packageResult = {
satisfied: !missing.includes(`${pkg}@${constraint}`) && !mismatched.some((m) => m.startsWith(`${pkg}@`)),
missing: missing.filter((m) => m.startsWith(`${pkg}@`)),
mismatched: mismatched.filter((m) => m.startsWith(`${pkg}@`))
};
this.cache.set(cacheKey, packageResult);
}
return {
satisfied: missing.length === 0 && mismatched.length === 0,
missing,
mismatched
};
}
async checkPythonPackages(packages) {
const missing = [];
const mismatched = [];
if (process.env.MLLD_TEST_MODE === "true") {
return {
satisfied: true,
missing: [],
mismatched: []
};
}
for (const [pkg, constraint] of Object.entries(packages)) {
const cacheKey = `python:${pkg}:${constraint}`;
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (!cached.satisfied) {
missing.push(...cached.missing);
mismatched.push(...cached.mismatched);
}
continue;
}
try {
const result = execSync(`pip show ${pkg}`, {
encoding: "utf8",
stdio: "pipe"
});
const versionMatch = result.match(/Version:\s*(.+)/);
const version2 = versionMatch ? versionMatch[1].trim() : null;
if (!version2) {
missing.push(`${pkg}${constraint}`);
} else if (!satisfiesConstraint(version2, constraint)) {
mismatched.push(`${pkg}==${version2} (needs ${constraint})`);
}
} catch {
missing.push(`${pkg}${constraint}`);
}
const packageResult = {
satisfied: !missing.includes(`${pkg}${constraint}`) && !mismatched.some((m) => m.startsWith(`${pkg}==`)),
missing: missing.filter((m) => m === `${pkg}${constraint}`),
mismatched: mismatched.filter((m) => m.startsWith(`${pkg}==`))
};
this.cache.set(cacheKey, packageResult);
}
return {
satisfied: missing.length === 0 && mismatched.length === 0,
missing,
mismatched
};
}
};
__name(_DefaultDependencyChecker, "DefaultDependencyChecker");
var DefaultDependencyChecker = _DefaultDependencyChecker;
function satisfiesConstraint(version2, constraint) {
if (constraint === "*") {
return true;
}
if (constraint === version2) {
return true;
}
if (constraint.startsWith("^")) {
const constraintBase = constraint.substring(1);
return isCompatibleVersion(version2, constraintBase, "minor");
}
if (constraint.startsWith("~")) {
const constraintBase = constraint.substring(1);
return isCompatibleVersion(version2, constraintBase, "patch");
}
if (constraint.startsWith(">=")) {
const constraintBase = constraint.substring(2);
return compareVersions(version2, constraintBase) >= 0;
}
if (constraint.includes("||")) {
const parts = constraint.split("||").map((s) => s.trim());
return parts.some((part) => satisfiesConstraint(version2, part));
}
if (constraint.startsWith("==")) {
return version2 === constraint.substring(2);
}
if (constraint.startsWith(">=")) {
const constraintBase = constraint.substring(2);
return compareVersions(version2, constraintBase) >= 0;
}
return version2 === constraint;
}
__name(satisfiesConstraint, "satisfiesConstraint");
function isCompatibleVersion(version2, base, updateType) {
const vParts = version2.split(".").map(Number);
const bParts = base.split(".").map(Number);
if (updateType === "patch") {
return vParts[0] === bParts[0] && vParts[1] === bParts[1] && vParts[2] >= bParts[2];
}
if (updateType === "minor") {
return vParts[0] === bParts[0] && (vParts[1] > bParts[1] || vParts[1] === bParts[1] && vParts[2] >= bParts[2]);
}
return false;
}
__name(isCompatibleVersion, "isCompatibleVersion");
function compareVersions(v1, v2) {
const parts1 = v1.split(".").map(Number);
const parts2 = v2.split(".").map(Number);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const p1 = parts1[i] || 0;
const p2 = parts2[i] || 0;
if (p1 > p2) return 1;
if (p1 < p2) return -1;
}
return 0;
}
__name(compareVersions, "compareVersions");
async function checkDependencies(needs, checker, location) {
const allMissing = [];
const allMismatched = [];
if (needs.node) {
const result = await checker.checkNodePackages(needs.node);
allMissing.push(...result.missing);
allMismatched.push(...result.mismatched);
}
if (needs.python) {
const result = await checker.checkPythonPackages(needs.python);
allMissing.push(...result.missing);
allMismatched.push(...result.mismatched);
}
if (allMissing.length > 0 || allMismatched.length > 0) {
const messages = [];
if (allMissing.length > 0) {
messages.push(`Missing packages: ${allMissing.join(", ")}`);
}
if (allMismatched.length > 0) {
messages.push(`Version mismatches: ${allMismatched.join(", ")}`);
}
throw new MlldDependencyError(messages.join("\n"), allMissing, allMismatched, location);
}
}
__name(checkDependencies, "checkDependencies");
var asyncLocalStorage = new AsyncLocalStorage();
var _a;
var MetadataShelf = (_a = class {
constructor() {
__publicField(this, "shelf", /* @__PURE__ */ new Map());
}
/**
* Store LoadContentResult objects on the shelf before unwrapping
*/
storeMetadata(value) {
if (isLoadContentResultArray(value)) {
for (const item of value) {
if (isLoadContentResult(item)) {
this.shelf.set(item.content, item);
}
}
} else if (isLoadContentResult(value)) {
this.shelf.set(value.content, value);
}
}
/**
* Attempt to restore metadata to returned values from JS functions
*/
restoreMetadata(value) {
if (!Array.isArray(value)) {
return value;
}
const restored = [];
let hasRestorable = false;
for (const item of value) {
if (typeof item === "string" && this.shelf.has(item)) {
restored.push(this.shelf.get(item));
hasRestorable = true;
} else {
restored.push(item);
}
}
return hasRestorable ? restored : value;
}
/**
* Clear the shelf to prevent memory leaks
*/
clear() {
this.shelf.clear();
}
}, __name(_a, "MetadataShelf"), _a);
var _AutoUnwrapManager = class _AutoUnwrapManager {
/**
* Auto-unwrap LoadContentResult objects to their content property
* while preserving metadata on the thread-local shelf
*
* @param value - The value to potentially unwrap
* @returns The unwrapped content or the original value
*/
static unwrap(value) {
const shelf = asyncLocalStorage.getStore() || new MetadataShelf();
if (process.env.MLLD_DEBUG === "true" && (isLoadContentResult(value) || isLoadContentResultArray(value))) {
console.error("[AutoUnwrapManager.unwrap] Unwrapping:", {
type: isLoadContentResultArray(value) ? "LoadContentResultArray" : "LoadContentResult",
shelfInContext: !!asyncLocalStorage.getStore()
});
}
shelf.storeMetadata(value);
if (isLoadContentResult(value)) {
return value.content;
}
if (isLoadContentResultArray(value)) {
return value.map((item) => item.content);
}
return value;
}
/**
* Execute a function with metadata preservation
* Sets up a new async context with its own metadata shelf
*
* @param fn - The function to execute
* @returns The result with restored metadata if applicable
*/
static async executeWithPreservation(fn) {
const shelf = new MetadataShelf();
try {
const result = await asyncLocalStorage.run(shelf, fn);
if (process.env.MLLD_DEBUG === "true") {
console.error("[AutoUnwrapManager] Result before restoration:", result);
console.error("[AutoUnwrapManager] Shelf contents:", shelf.shelf);
}
const restored = shelf.restoreMetadata(result);
if (process.env.MLLD_DEBUG === "true") {
console.error("[AutoUnwrapManager] Result after restoration:", restored);
}
return restored;
} finally {
shelf.clear();
}
}
/**
* Restore metadata from the current context's shelf
* Used when we have a result that might contain unwrapped content
*
* @param value - The value to potentially restore
* @returns The value with restored metadata if applicable
*/
static restore(value) {
const shelf = asyncLocalStorage.getStore();
if (!shelf) {
return value;
}
return shelf.restoreMetadata(value);
}
/**
* Clear the current context's shelf
* Should be called after restoration to prevent memory leaks
*/
static clear() {
const shelf = asyncLocalStorage.getStore();
if (shelf) {
shelf.clear();
}
}
};
__name(_AutoUnwrapManager, "AutoUnwrapManager");
var AutoUnwrapManager = _AutoUnwrapManager;
// interpreter/eval/run.ts
function determineTaintLevel(nodes, env) {
for (const node of nodes) {
if (node.type === "VariableReference") {
const varName = node.identifier;
if (varName) {
return TaintLevel.REGISTRY_WARNING;
}
}
}
return TaintLevel.TRUSTED;
}
__name(determineTaintLevel, "determineTaintLevel");
async function evaluateRun(directive, env, callStack = []) {
let output = "";
const executionContext = {
sourceLocation: directive.location,
directiveNode: directive,
filePath: env.getCurrentFilePath(),
directiveType: directive.meta?.directiveType || "run"
};
if (directive.subtype === "runCommand") {
const commandNodes = directive.values?.identifier || directive.values?.command;
if (!commandNodes) {
throw new Error("Run command directive missing command");
}
const command = await interpolate(commandNodes, env, InterpolationContext.ShellCommand);
const security = env.getSecurityManager();
if (security) {
const taintLevel = determineTaintLevel(commandNodes);
const securityManager = security;
const analyzer = securityManager.commandAnalyzer;
if (analyzer) {
const analysis = await analyzer.analyze(command, taintLevel);
if (analysis.blocked) {
const reason = analysis.risks[0]?.description || "Security policy violation";
throw new MlldCommandExecutionError(`Security: Command blocked - ${reason}`, directive.location, {
command,
exitCode: 1,
duration: 0,
stderr: `This command is blocked by security policy: ${reason}`,
workingDirectory: env.getBasePath(),
directiveType: "run"
}, env);
}
if (taintLevel === TaintLevel.LLM_OUTPUT) {
throw new MlldCommandExecutionError("Security: Cannot execute LLM-generated commands", directive.location, {
command,
exitCode: 1,
duration: 0,
stderr: "Commands generated by LLMs cannot be executed for security reasons",
workingDirectory: env.getBasePath(),
directiveType: "run"
}, env);
}
}
}
output = await env.executeCommand(command, void 0, executionContext);
} else if (directive.subtype === "runCode") {
const codeNodes = directive.values?.code;
if (!codeNodes) {
throw new Error("Run code directive missing code");
}
const code = await interpolate(codeNodes, env, InterpolationContext.Default);
const args = directive.values?.args || [];
const argValues = {};
if (args.length > 0) {
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg && typeof arg === "object" && arg.type === "VariableReference") {
const varName = arg.identifier;
const variable = env.getVariable(varName);
if (!variable) {
throw new Error(`Variable not found: ${varName}`);
}
const { extractVariableValue: extractVariableValue3 } = await import('./variable-resolution-SVHB4DCD.mjs');
const value = await extractVariableValue3(variable, env);
const unwrappedValue = AutoUnwrapManager.unwrap(value);
argValues[varName] = unwrappedValue;
} else if (typeof arg === "string") {
argValues[`arg${i}`] = arg;
}
}
}
const language = directive.meta?.language || "javascript";
output = await AutoUnwrapManager.executeWithPreservation(async () => {
return await env.executeCode(code, language, argValues, executionContext);
});
} else if (directive.subtype === "runExec") {
const identifierNodes = directive.values?.identifier;
if (!identifierNodes || !Array.isArray(identifierNodes) || identifierNodes.length === 0) {
throw new Error("Run exec directive missing exec reference");
}
let commandName = "";
const identifierNode = identifierNodes[0];
if (identifierNode.type === "VariableReference" && "identifier" in identifierNode) {
commandName = identifierNode.identifier;
}
if (commandName && !callStack.includes(commandName)) {
callStack = [
...callStack,
commandName
];
}
let execVar;
if (identifierNode.type === "VariableReference" && identifierNode.fields && identifierNode.fields.length > 0) {
const varRef = identifierNode;
const baseVar = env.getVariable(varRef.identifier);
if (!baseVar) {
throw new Error(`Base variable not found: ${varRef.identifier}`);
}
const { extractVariableValue: extractVariableValue3 } = await import('./variable-resolution-SVHB4DCD.mjs');
let value = await extractVariableValue3(baseVar, env);
for (const field of varRef.fields) {
if ((field.type === "field" || field.type === "stringIndex" || field.type === "numericField") && typeof value === "object" && value !== null) {
value = value[String(field.value)];
} else if (field.type === "arrayIndex" && Array.isArray(value)) {
value = value[Number(field.value)];
} else {
const fieldName = String(field.value);
throw new Error(`Cannot access field '${fieldName}' on ${typeof value}`);
}
}
if (process.env.DEBUG_EXEC || process.env.NODE_ENV === "test") {
console.error("[DEBUG] Field access resolved value:", {
identifier: varRef.identifier,
fields: varRef.fields.map((f) => ({
type: f.type,
value: f.value
})),
valueType: typeof value,
hasExecutable: value && typeof value === "object" && "__executable" in value,
hasType: value && typeof value === "object" && "type" in value,
valueKeys: value && typeof value === "object" ? Object.keys(value) : [],
hasExecutableDef: value && typeof value === "object" && "executableDef" in value,
executableDef: value && typeof value === "object" ? value.executableDef : void 0,
value: process.env.DEBUG_EXEC === "verbose" ? value : void 0
});
}
if (typeof value === "object" && value !== null && "type" in value && value.type === "executable") {
execVar = value;
} else if (typeof value === "object" && value !== null && "__executable" in value && value.__executable) {
const fullName = `${varRef.identifier}.${varRef.fields.map((f) => f.value).join(".")}`;
let capturedShadowEnvs = value.metadata?.capturedShadowEnvs;
if (capturedShadowEnvs && typeof capturedShadowEnvs === "object") {
const deserialized = {};
for (const [lang, shadowObj] of Object.entries(capturedShadowEnvs)) {
if (shadowObj && typeof shadowObj === "object") {
const map = /* @__PURE__ */ new Map();
for (const [name, func] of Object.entries(shadowObj)) {
map.set(name, func);
}
deserialized[lang] = map;
}
}
capturedShadowEnvs = deserialized;
}
execVar = {
type: "executable",
name: fullName,
value: value.value || {
type: "code",
template: "",
language: "js"
},
paramNames: value.paramNames || [],
source: {
directive: "import",
syntax: "code",
hasInterpolation: false,
isMultiLine: false
},
createdAt: Date.now(),
modifiedAt: Date.now(),
metadata: {
...value.metadata || {},
executableDef: value.executableDef,
// CRITICAL: Preserve captured shadow environments from imports (deserialized)
capturedShadowEnvs
}
};
if (process.env.DEBUG_EXEC) {
console.error("[DEBUG] Reconstructed executable metadata:", {
name: fullName,
hasMetadata: !!value.metadata,
hasCapturedShadowEnvs: !!capturedShadowEnvs,
capturedShadowEnvKeys: capturedShadowEnvs ? Object.keys(capturedShadowEnvs) : [],
deserializedLangs: capturedShadowEnvs ? Object.entries(capturedShadowEnvs).map(([lang, map]) => `${lang}: ${map instanceof Map ? map.size : 0} functions`) : []
});
}
} else if (typeof value === "string") {
const variable = env.getVariable(value);
if (!variable || !isExecutableVariable(variable)) {
throw new Error(`Executable variable not found: ${value}`);
}
execVar = variable;
} else {
throw new Error(`Field access did not resolve to an executable: ${typeof value}, got: ${JSON.stringify(value)}`);
}
} else {
if (!commandName) {
throw new Error("Run exec directive identifier must be a command reference");
}
const variable = env.getVariable(commandName);
if (!variable || !isExecutableVariable(variable)) {
throw new Error(`Executable variable not found: ${commandName}`);
}
execVar = variable;
}
const definition = execVar.metadata?.executableDef;
if (!definition) {
const fullPath = identifierNode.type === "VariableReference" && identifierNode.fields && identifierNode.fields.length > 0 ? `${identifierNode.identifier}.${identifierNode.fields.map((f) => f.value).join(".")}` : commandName;
throw new Error(`Executable ${fullPath} has no definition in metadata`);
}
const args = directive.values?.args || [];
const argValues = {};
const paramNames = definition.paramNames;
if (paramNames && paramNames.length > 0) {
for (let i = 0; i < paramNames.length; i++) {
const paramName = paramNames[i];
if (!args[i]) {
argValues[paramName] = "";
continue;
}
const arg = args[i];
let argValue;
if (typeof arg === "string" || typeof arg === "number" || typeof arg === "boolean") {
argValue = String(arg);
} else if (arg && typeof arg === "object" && "type" in arg) {
argValue = await interpolate([
arg
], env, InterpolationContext.Default);
} else {
argValue = String(arg);
}
argValues[paramName] = argValue;
}
}
if (definition.type === "command" && "commandTemplate" in definition) {
const tempEnv = env.createChild();
for (const [key, value] of Object.entries(argValues)) {
tempEnv.setParameterVariable(key, createSimpleTextVariable(key, value));
}
const cleanTemplate = definition.commandTemplate.map((seg, idx) => {
if (idx === 0 && seg.type === "Text" && "content" in seg && seg.content.startsWith("[")) {
return {
...seg,
content: seg.content.substring(1)
};
}
return seg;
});
const command = await interpolate(cleanTemplate, tempEnv, InterpolationContext.ShellCommand);
const security = env.getSecurityManager();
if (security) {
const securityManager = security;
const analyzer = securityManager.commandAnalyzer;
if (analyzer) {
const analysis = await analyzer.analyze(command);
if (analysis.blocked) {
const reason = analysis.risks?.[0]?.description || "Security policy violation";
throw new MlldCommandExecutionError(`Security: Exec command blocked - ${reason}`, directive.location, {
command,
exitCode: 1,
duration: 0,
stderr: `This exec command is blocked by security policy: ${reason}`,
workingDirectory: env.getBasePath(),
directiveType: "run"
}, env);
}
}
}
output = await env.executeCommand(command, void 0, executionContext);
} else if (definition.type === "commandRef") {
const refExecVar = env.getVariable(definition.commandRef);
if (!refExecVar || !isExecutableVariable(refExecVar)) {
throw new Error(`Referenced executable not found: ${definition.commandRef}`);
}
if (callStack.includes(definition.commandRef)) {
const cycle = [
...callStack,
definition.commandRef
].join(" -> ");
throw new Error(`Circular command reference detected: ${cycle}`);
}
const refDirective = {
...directive,
values: {
...directive.values,
identifier: [
{
type: "Text",
content: definition.commandRef
}
],
args: definition.commandArgs
}
};
const result = await evaluateRun(refDirective, env, callStack);
output = result.value;
} else if (definition.type === "code") {
const tempEnv = env.createChild();
for (const [key, value] of Object.entries(argValues)) {
tempEnv.setParameterVariable(key, createSimpleTextVariable(key, value));
}
const code = await interpolate(definition.codeTemplate, tempEnv, InterpolationContext.Default);
if (process.env.DEBUG_EXEC) {
logger.debug("run.ts code execution debug:", {
codeTemplate: definition.codeTemplate,
interpolatedCode: code,
argValues
});
}
const codeParams = {
...argValues
};
const capturedEnvs = execVar.metadata?.capturedShadowEnvs;
if (capturedEnvs && (definition.language === "js" || definition.language === "javascript" || definition.language === "node" || definition.language === "nodejs")) {
codeParams.__capturedShadowEnvs = capturedEnvs;
}
output = await AutoUnwrapManager.executeWithPreservation(async () => {
return await env.executeCode(code, definition.language || "javascript", codeParams, executionContext);
});
} else if (definition.type === "template") {
const tempEnv = env.createChild();
for (const [key, value] of Object.entries(argValues)) {
tempEnv.setParameterVariable(key, createSimpleTextVariable(key, value));
}
output = await interpolate(definition.template, tempEnv, InterpolationContext.Default);
} else {
throw new Error(`Unsupported executable type: ${definition.type}`);
}
} else if (directive.subtype === "runExecInvocation") {
const execInvocation = directive.values?.execInvocation;
if (!execInvocation) {
throw new Error("Run exec invocation directive missing exec invocation");
}
const { evaluateExecInvocation } = await import('./exec-invocation-BKHC2VUW.mjs');
const result = await evaluateExecInvocation(execInvocation, env);
output = String(result.value);
} else if (directive.subtype === "runExecReference") {
const execRef = directive.values?.execRef;
if (!execRef) {
throw new Error("Run exec reference directive missing exec reference");
}
const { evaluateExecInvocation } = await import('./exec-invocation-BKHC2VUW.mjs');
const result = await evaluateExecInvocation(execRef, env);
output = String(result.value);
} else {
throw new Error(`Unsupported run subtype: ${directive.subtype}`);
}
const withClause = directive.meta?.withClause || directive.values?.withClause;
if (withClause) {
if (withClause.needs) {
const checker = new DefaultDependencyChecker();
await checkDependencies(withClause.needs, checker, directive.location);
}
if (withClause.pipeline && withClause.pipeline.length > 0) {
const format = withClause.format;
output = await executePipeline(output, withClause.pipeline, env, directive.location, format);
}
}
if (!output.endsWith("\n")) {
output += "\n";
}
if (!directive.meta?.isDataValue && !directive.meta?.isEmbedded) {
const replacementNode = {
type: "Text",
nodeId: `${directive.nodeId}-output`,
content: output
};
env.addNode(replacementNode);
}
return {
value: output,
env
};
}
__name(evaluateRun, "evaluateRun");
// interpreter/eval/import/ImportPathResolver.ts
var _ImportPathResolver = class _ImportPathResolver {
constructor(env) {
__publicField(this, "env");
this.env = env;
}
/**
* Resolve import path and determine the type of import
*/
async resolveImportPath(directive) {
const pathValue = directive.values?.path;
if (!pathValue) {
throw new Error("Import directive missing path");
}
const pathNodes = this.normalizePathNodes(pathValue, directive);
const specialImport = await this.detectSpecialImports(pathNodes, directive);
if (specialImport) {
return specialImport;
}
const importPath = await this.interpolatePathNodes(pathNodes);
return this.routeImportRequest(importPath, pathNodes, directive);
}
/**
* Convert path value to consistent node array format
*/
normalizePathNodes(pathValue, directive) {
if (typeof pathValue === "string") {
return [
{
type: "Text",
content: pathValue,
nodeId: "",
location: directive.location
}
];
} else if (Array.isArray(pathValue)) {
return pathValue;
} else if (pathValue && typeof pathValue === "object" && pathValue.type === "path") {
if (pathValue.subtype === "urlPath" && pathValue.values?.url) {
return pathValue.values.url;
} else if (pathValue.values?.path) {
return pathValue.values.path;
} else {
throw new Error("Invalid path object structure in import directive");
}
} else {
throw new Error("Import directive path must be a string, array of nodes, or path object");
}
}
/**
* Detect special imports (@input, @stdin, resolver imports)
*/
async detectSpecialImports(pathNodes, directive) {
if (pathNodes.length === 0) {
return null;
}
const firstNode = pathNodes[0];
if (firstNode.type === "Text") {
const content = firstNode.content;
if (content === "@INPUT" || content === "@input") {
return {
type: "input",
resolvedPath: "@input",
resolverName: "input"
};
} else if (content === "@stdin") {
return {
type: "input",
resolvedPath: "@input",
resolverName: "input"
};
}
}
if (firstNode.type === "VariableReference") {
const varRef = firstNode;
if (varRef.identifier === "INPUT" || varRef.identifier === "input") {
return {
type: "input",
resolvedPath: "@input",
resolverName: "input"
};
} else if (varRef.identifier === "stdin") {
return {
type: "input",
resolvedPath: "@input",
resolverName: "input"
};
}
if (varRef.isSpecial && varRef.identifier) {
const resolverManager = this.env.getResolverManager();
if (resolverManager && resolverManager.isResolverName(varRef.identifier)) {
return {
type: "resolver",
resolvedPath: `@${varRef.identifier}`,
resolverName: varRef.identifier
};
}
}
}
return null;
}
/**
* Interpolate path nodes to get the final import path
*/
async interpolatePathNodes(pathNodes) {
return (await interpolate(pathNodes, this.env)).trim();
}
/**
* Route import request based on the interpolated path
*/
async routeImportRequest(importPath, pathNodes, directive) {
const sectionNodes = directive.values?.section;
let sectionName;
if (sectionNodes && Array.isArray(sectionNodes)) {
sectionName = await interpolate(sectionNodes, this.env);
}
if (importPath.startsWith("@")) {
return this.handleModuleReference(importPath, directive, sectionName);
}
const pathNode = pathNodes[0];
const isURL = pathNode?.subtype === "urlPath" || pathNode?.subtype === "urlSectionPath" || this.env.isURL(importPath);
if (isURL) {
return this.handleURLImport(importPath, directive, sectionName);
}
return this.handleFileImport(importPath, directive, sectionName);
}
/**
* Handle module reference imports (@user/module, @now, etc.)
*/
async handleModuleReference(importPath, directive, sectionName) {
const resolverManager = this.env.getResolverManager();
const potentialResolverName = importPath.substring(1);
if (resolverManager && resolverManager.isResolverName(potentialResolverName)) {
return {
type: "resolver",
resolvedPath: importPath,
resolverName: potentialResolverName,
sectionName
};
}
const { moduleRef, expectedHash } = this.extractHashFromPath(importPath, directive);
return {
type: "module",
resolvedPath: moduleRef,
expectedHash,
sectionName
};
}
/**
* Handle URL imports
*/
async handleURLImport(importPath, directive, sectionName) {
const pathMeta = directive.meta?.path;
const expectedHash = pathMeta?.hash;
return {
type: "url",
resolvedPath: importPath,
expectedHash,
sectionName
};
}
/**
* Handle file path imports
*/
async handleFileImport(importPath, directive, sectionName) {
const resolvedPath = await this.env.resolvePath(importPath);
const pathMeta = directive.meta?.path;
const expectedHash = pathMeta?.hash;
return {
type: "file",
resolvedPath,
expectedHash,
sectionName
};
}
/**
* Extract hash information from module reference
*/
extractHashFromPath(importPath, directive) {
let moduleRef = importPath;
let expectedHash;
const pathMeta = directive.meta?.path;
if (pathMeta && pathMeta.hash) {
expectedHash = pathMeta.hash;
const hashIndex = importPath.lastIndexOf("@");
if (hashIndex > 0) {
moduleRef = importPath.substring(0, hashIndex);
}
}
return {
moduleRef,
expectedHash
};
}
/**
* Check if a path is a URL
*/
isURLPath(path5) {
return this.env.isURL(path5);
}
/**
* Liberal import syntax support - handles both quoted and unquoted module references
* This follows Postel's Law: "be liberal in what you accept"
*/
handleLiberalImportSyntax(pathNodes) {
return pathNodes;
}
};
__name(_ImportPathResolver, "ImportPathResolver");
var ImportPathResolver = _ImportPathResolver;
var _HashUtils = class _HashUtils {
/**
* Generate SHA-256 hash of content
* @param content - The content to hash
* @returns 64-character hex string hash
*/
static hash(content) {
return crypto.createHash("sha256").update(content).digest("hex");
}
/**
* Generate SRI-style integrity hash (sha256-base64)
* @param content - The content to hash
* @returns SRI-formatted integrity string
*/
static integrity(content) {
const hash = crypto.createHash("sha256").update(content).digest("base64");
return `sha256-${hash}`;
}
/**
* Verify content against a hash
* @param content - The content to verify
* @param expectedHash - The expected SHA-256 hash (hex)
* @returns true if content matches hash
*/
static verify(content, expectedHash) {
const actualHash = this.hash(content);
return actualHash === expectedHash;
}
/**
* Verify content against SRI integrity string
* @param content - The content to verify
* @param integrity - The SRI integrity string (e.g., "sha256-...")
* @returns true if content matches integrity
*/
static verifyIntegrity(content, integrity) {
const actualIntegrity = this.integrity(content);
return actualIntegrity === integrity;
}
/**
* Get short hash (first n characters)
* @param fullHash - The full 64-character hash
* @param length - Number of characters (default 8)
* @returns Short hash string
*/
static shortHash(fullHash, length = 8) {
return fullHash.substring(0, length);
}
/**
* Expand short hash to full hash by searching cache
* @param shortHash - The short hash to expand
* @param availableHashes - List of full hashes to search
* @returns Full hash if unique match found, null otherwise
*/
static expandHash(shortHash, availableHashes) {
const matches = availableHashes.filter((hash) => hash.startsWith(shortHash));
if (matches.length === 1) {
return matches[0];
} else if (matches.length === 0) {
return null;
} else {
throw new Error(`Ambiguous short hash '${shortHash}' matches ${matches.length} hashes`);
}
}
/**
* Get cache directory path for a hash
* Uses first 2 characters as subdirectory for better filesystem performance
* @param hash - The full hash
* @returns Path components [prefix, rest] for directory structure
*/
static getCachePathComponents(hash) {
return {
prefix: hash.substring(0, 2),
rest: hash.substring(2)
};
}
/**
* Create module content object with hash
* @param content - The module content
* @param source - The source URL/path
* @returns ModuleContent object
*/
static createModuleContent(content, source) {
return {
content,
hash: this.hash(content),
metadata: {
source,
timestamp: /* @__PURE__ */ new Date(),
size: Buffer.byteLength(content, "utf8")
}
};
}
/**
* Compare two hashes in constant time to prevent timing attacks
* @param hash1 - First hash
* @param hash2 - Second hash
* @returns true if hashes match
*/
static secureCompare(hash1, hash2) {
if (hash1.length !== hash2.length) {
return false;
}
const buffer1 = Buffer.from(hash1);
const buffer2 = Buffer.from(hash2);
return crypto.timingSafeEqual(buffer1, buffer2);
}
};
__name(_HashUtils, "HashUtils");
var HashUtils = _HashUtils;
var _ImportSecurityValidator = class _ImportSecurityValidator {
constructor(env) {
__publicField(this, "env");
this.env = env;
}
/**
* Validates import security including circular imports, content hashes,
* version compatibility, and import approval
*/
async validateImportSecurity(resolution, content) {
const validation = {
approved: true,
hashValid: true,
versionCompatible: true,
circularImportDetected: false,
errors: []
};
validation.circularImportDetected = this.checkCircularImports(resolution.resolvedPath);
if (validation.circularImportDetected) {
validation.errors.push(`Circular import detected: ${resolution.resolvedPath}`);
return validation;
}
if (resolution.expectedHash && content) {
validation.hashValid = this.validateContentHash(content, resolution.expectedHash, resolution.resolvedPath);
if (!validation.hashValid) {
validation.errors.push(`Hash validation failed for: ${resolution.resolvedPath}`);
}
}
return validation;
}
/**
* Checks for circular import dependencies
*/
checkCircularImports(resolvedPath) {
return this.env.isImporting(resolvedPath);
}
/**
* Validates content hash against expected hash (supports both full and short hashes)
*/
validateContentHash(content, expectedHash, resolvedPath) {
const isTestMode = process.env.MLLD_SKIP_HASH_VALIDATION === "true";
if (isTestMode) {
return true;
}
const actualHash = HashUtils.hash(content);
const shortActualHash = HashUtils.shortHash(actualHash, expectedHash.length);
if (expectedHash.length < 64) {
if (shortActualHash !== expectedHash) {
throw new Error(`Module hash mismatch for '${resolvedPath}': expected ${expectedHash}, got ${shortActualHash} (full: ${actualHash})`);
}
} else {
if (!HashUtils.secureCompare(actualHash, expectedHash)) {
throw new Error(`Module hash mismatch for '${resolvedPath}': expected ${expectedHash}, got ${actualHash}`);
}
}
return true;
}
/**
* Checks mlld version compatibility from frontmatter
*/
checkVersionCompatibility(frontmatterData, resolvedPath) {
const requiredVersion = frontmatterData["mlld-version"] || frontmatterData["mlldVersion"] || frontmatterData["mlld_version"];
if (!requiredVersion) {
return true;
}
if (process.env.MLLD_DEBUG_VERSION) {
logger.debug(`[Version Check] Module requires: ${requiredVersion}, Current: ${version}`);
}
const versionCheck = checkMlldVersion(requiredVersion);
if (!versionCheck.compatible) {
const moduleName = frontmatterData.module || frontmatterData.name || path3.basename(resolvedPath);
throw new MlldError(formatVersionError(moduleName, requiredVersion, version), {
code: "VERSION_MISMATCH",
severity: "error",
module: moduleName,
requiredVersion,
path: resolvedPath
});
}
return true;
}
/**
* Requests import approval for URL imports if needed
*/
async requestImportApproval(resolvedPath) {
const isURL = this.env.isURL(resolvedPath);
if (isURL) {
return true;
}
return true;
}
/**
* Marks the beginning of an import for circular detection
*/
beginImport(resolvedPath) {
const isURL = this.env.isURL(resolvedPath);
if (isURL) {
this.env.beginImport(resolvedPath);
}
}
/**
* Marks the end of an import for circular detection
*/
endImport(resolvedPath) {
const isURL = this.env.isURL(resolvedPath);
if (isURL) {
this.env.endImport(resolvedPath);
}
}
/**
* Validates overall module integrity combining all security checks
*/
async validateModuleIntegrity(resolution, content, frontmatterData) {
if (this.checkCircularImports(resolution.resolvedPath)) {
throw new Error(`Circular import detected: ${resolution.resolvedPath}`);
}
if (resolution.expectedHash) {
this.validateContentHash(content, resolution.expectedHash, resolution.resolvedPath);
}
if (frontmatterData) {
this.checkVersionCompatibility(frontmatterData, resolution.resolvedPath);
}
}
/**
* Validates content security excluding circular import checks
* (Used when import tracking is already in progress)
*/
async validateContentSecurity(resolution, content, frontmatterData) {
if (resolution.expectedHash) {
this.validateContentHash(content, resolution.expectedHash, resolution.resolvedPath);
}
if (frontmatterData) {
this.checkVersionCompatibility(frontmatterData, resolution.resolvedPath);
}
}
};
__name(_ImportSecurityValidator, "ImportSecurityValidator");
var ImportSecurityValidator = _ImportSecurityValidator;
var _ModuleContentProcessor = class _ModuleContentProcessor {
constructor(env, securityValidator, variableImporter) {
__publicField(this, "env");
__publicField(this, "securityValidator");
__publicField(this, "variableImporter");
this.env = env;
this.securityValidator = securityValidator;
this.variableImporter = variableImporter;
}
/**
* Process module content from reading through evaluation
*/
async processModuleContent(resolution, directive) {
const { resolvedPath } = resolution;
const isURL = this.env.isURL(resolvedPath);
this.securityValidator.beginImport(resolvedPath);
try {
const content = await this.readContentFromSource(resolvedPath, isURL);
this.env.cacheSource(resolvedPath, content);
await this.securityValidator.validateContentSecurity(resolution, content);
const parseResult = await this.parseContentByType(content, resolvedPath, directive);
if (resolvedPath.endsWith(".json")) {
return this.processJSONContent(parseResult, directive, resolvedPath);
}
return this.processMLLDContent(parseResult, resolvedPath, isURL);
} finally {
this.securityValidator.endImport(resolvedPath);
}
}
/**
* Process module content from resolver (content already fetched)
*/
async processResolverContent(content, ref, directive) {
this.securityValidator.beginImport(ref);
try {
if (process.env.MLLD_DEBUG === "true") {
console.log(`[ModuleContentProcessor] Processing resolver content for: ${ref}`);
console.log(`[ModuleContentProcessor] Content length: ${content.length}`);
console.log(`[ModuleContentProcessor] Content preview: ${content.substring(0, 200)}`);
}
this.env.cacheSource(ref, content);
const parseResult = await this.parseContentByType(content, ref, directive);
if (ref.endsWith(".json")) {
return this.processJSONContent(parseResult, directive, ref);
}
const result = await this.processMLLDContent(parseResult, ref, false);
if (process.env.MLLD_DEBUG === "true") {
console.log(`[ModuleContentProcessor] Module object keys: ${Object.keys(result.moduleObject).join(", ")}`);
console.log(`[ModuleContentProcessor] Has frontmatter: ${result.frontmatter !== null}`);
console.log(`[ModuleContentProcessor] Child env vars: ${result.childEnvironment.getCurrentVariables().size}`);
console.log(`[ModuleContentProcessor] Child env var names: ${Array.from(result.childEnvironment.getCurrentVariables().keys()).join(", ")}`);
}
return result;
} finally {
this.securityValidator.endImport(ref);
}
}
/**
* Read content from file or URL
*/
async readContentFromSource(resolvedPath, isURL) {
try {
return isURL ? await this.env.fetchURL(resolvedPath, true) : await this.env.readFile(resolvedPath);
} catch (error) {
throw new Error(`Failed to read imported file '${resolvedPath}': ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Parse content by type (JSON vs mlld vs text)
*/
async parseContentByType(content, resolvedPath, directive) {
if (resolvedPath.endsWith(".json")) {
try {
return JSON.parse(content);
} catch (error) {
throw new Error(`Failed to parse JSON file '${resolvedPath}': ${error instanceof Err