UNPKG

mlld

Version:

mlld: a modular prompt scripting language

1,340 lines (1,336 loc) 340 kB
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