UNPKG

mlld

Version:

mlld: llm scripting language

727 lines (725 loc) 28.9 kB
import { MlldImportError } from './chunk-V5XE5YB5.mjs'; import { astLocationToSourceLocation } from './chunk-6PZVQRSR.mjs'; import { makeSecurityDescriptor, varMxToSecurityDescriptor, mergeDescriptors, VariableMetadataUtils, isStructuredValue, createStructuredValueVariable, createTemplateVariable, createArrayVariable, createObjectVariable, createImportedVariable, createExecutableVariable } from './chunk-RKGZ44GZ.mjs'; import { __name, __publicField } from './chunk-NJQT543K.mjs'; import * as fs from 'fs'; var _VariableImporter = class _VariableImporter { constructor(objectResolver) { __publicField(this, "objectResolver"); this.objectResolver = objectResolver; } /** * Serialize shadow environments for export (Maps to objects) * WHY: Maps don't serialize to JSON, so we convert them to plain objects * GOTCHA: Function references are preserved directly */ serializeShadowEnvs(envs) { const result = {}; for (const [lang, shadowMap] of Object.entries(envs)) { if (shadowMap instanceof Map && shadowMap.size > 0) { const obj = {}; for (const [name, func] of shadowMap) { obj[name] = func; } result[lang] = obj; } } return result; } /** * Deserialize shadow environments after import (objects to Maps) * WHY: Shadow environments are expected as Maps internally */ deserializeShadowEnvs(envs) { const result = {}; for (const [lang, shadowObj] of Object.entries(envs)) { if (shadowObj && typeof shadowObj === "object") { const map = /* @__PURE__ */ new Map(); for (const [name, func] of Object.entries(shadowObj)) { map.set(name, func); } result[lang] = map; } } return result; } /** * Checks whether the requested alias has already been claimed during the * current import pass and throws a detailed error when a collision exists. */ ensureImportBindingAvailable(targetEnv, name, importSource, location) { if (!name || name.trim().length === 0) return; const existingBinding = targetEnv.getImportBinding(name); if (!existingBinding) { return; } throw new MlldImportError(`Import collision - '${name}' already imported from ${existingBinding.source}. Alias one of the imports.`, { code: "IMPORT_NAME_CONFLICT", context: { name, existingSource: existingBinding.source, attemptedSource: importSource, existingLocation: existingBinding.location, newLocation: location, suggestion: "Use 'as' to alias one of the imports" }, details: { filePath: location?.filePath || existingBinding.location?.filePath, variableName: name } }); } /** * Writes the variable and persists the associated binding only after the * assignment succeeds, preventing partially-applied imports from polluting * the collision tracking map. */ setVariableWithImportBinding(targetEnv, alias, variable, binding) { let shouldPersistBinding = false; try { targetEnv.setVariable(alias, variable); shouldPersistBinding = true; } finally { if (shouldPersistBinding) { targetEnv.setImportBinding(alias, binding); } } } /** * Serialize module environment for export (Map to object) * WHY: Maps don't serialize to JSON, so we need to convert to exportable format * IMPORTANT: Use the exact same serialization as processModuleExports to ensure compatibility */ serializeModuleEnv(moduleEnv) { const tempResult = this.processModuleExports(moduleEnv, {}, true, null, void 0, void 0, moduleEnv); return tempResult.moduleObject; } /** * Deserialize module environment after import (object to Map) * IMPORTANT: Reuse createVariableFromValue to ensure proper Variable reconstruction */ deserializeModuleEnv(moduleEnv) { const result = /* @__PURE__ */ new Map(); if (moduleEnv && typeof moduleEnv === "object") { for (const [name, varData] of Object.entries(moduleEnv)) { const variable = this.createVariableFromValue(name, varData, "module-env", name); result.set(name, variable); } } return result; } /** * Import variables from a processing result into the target environment */ async importVariables(processingResult, directive, targetEnv) { const { moduleObject } = processingResult; const serializedMetadata = this.extractMetadataMap(moduleObject); const moduleObjectForImport = serializedMetadata ? Object.fromEntries(Object.entries(moduleObject).filter(([key]) => key !== "__metadata__")) : moduleObject; await this.handleImportType(directive, moduleObjectForImport, targetEnv, processingResult.childEnvironment, serializedMetadata, processingResult.guardDefinitions); } /** * Process module exports - either use explicit @data module or auto-generate */ processModuleExports(childVars, parseResult, skipModuleEnvSerialization, manifest, childEnv, options, currentSerializationTarget) { const frontmatter = parseResult.frontmatter || null; const moduleObject = {}; const serializedMetadataMap = {}; const manifestEntries = manifest?.hasEntries() ? manifest.getEntries() : []; const variableEntries = manifestEntries.filter((entry) => entry.kind !== "guard"); const guardEntries = manifestEntries.filter((entry) => entry.kind === "guard"); const explicitNames = variableEntries.length > 0 ? variableEntries.map((entry) => entry.name) : null; const explicitExports = explicitNames ? new Set(explicitNames) : null; if (explicitNames && explicitNames.length > 0) { for (const name of explicitNames) { if (!childVars.has(name)) { const location = manifest?.getLocation(name); throw new MlldImportError(`Exported name '${name}' is not defined in this module`, { code: "EXPORTED_NAME_NOT_FOUND", context: { exportName: name, location }, details: { filePath: location?.filePath, variableName: name } }); } } } const guardNames = guardEntries.map((entry) => entry.name); if (guardNames.length > 0) { if (!childEnv) { throw new MlldImportError("Guard exports require a child environment", { code: "GUARD_EXPORT_CONTEXT", details: { guards: guardNames } }); } for (const entry of guardEntries) { const definition = childEnv.getGuardRegistry().getByName(entry.name); if (!definition) { const location = manifest?.getLocation(entry.name); throw new MlldImportError(`Exported guard '${entry.name}' is not defined in this module`, { code: "EXPORTED_GUARD_NOT_FOUND", context: { guardName: entry.name, location }, details: { filePath: location?.filePath, variableName: entry.name } }); } } } const shouldSerializeModuleEnv = !skipModuleEnvSerialization; let moduleEnvSnapshot = null; const getModuleEnvSnapshot = /* @__PURE__ */ __name(() => { if (!moduleEnvSnapshot) { moduleEnvSnapshot = new Map(childVars); } return moduleEnvSnapshot; }, "getModuleEnvSnapshot"); if (process.env.MLLD_DEBUG === "true") { console.log(`[processModuleExports] childVars size: ${childVars.size}`); console.log(`[processModuleExports] childVars keys: ${Array.from(childVars.keys()).join(", ")}`); } const envSnapshot = childEnv?.getSecuritySnapshot?.(); const envDescriptor = envSnapshot ? makeSecurityDescriptor({ labels: envSnapshot.labels, taint: envSnapshot.taint, sources: envSnapshot.sources, policyContext: envSnapshot.policy ? { ...envSnapshot.policy } : void 0 }) : void 0; for (const [name, variable] of childVars) { if (explicitExports && !explicitExports.has(name)) { continue; } if (!this.isLegitimateVariableForExport(variable)) { if (process.env.MLLD_DEBUG === "true") { console.log(`[processModuleExports] Skipping non-legitimate variable '${name}' with type: ${variable.type}`); } continue; } if (variable.type === "executable") { const execVar = variable; let serializedInternal = { ...execVar.internal ?? {} }; if (serializedInternal.capturedShadowEnvs) { serializedInternal = { ...serializedInternal, capturedShadowEnvs: this.serializeShadowEnvs(serializedInternal.capturedShadowEnvs) }; } if (shouldSerializeModuleEnv) { const capturedEnv = serializedInternal.capturedModuleEnv instanceof Map ? serializedInternal.capturedModuleEnv : getModuleEnvSnapshot(); serializedInternal = { ...serializedInternal, capturedModuleEnv: this.serializeModuleEnv(capturedEnv) }; } else { const existingCapture = serializedInternal.capturedModuleEnv; if (existingCapture instanceof Map) { if (currentSerializationTarget && existingCapture === currentSerializationTarget) { delete serializedInternal.capturedModuleEnv; } else { serializedInternal = { ...serializedInternal, capturedModuleEnv: this.serializeModuleEnv(existingCapture) }; } } } moduleObject[name] = { __executable: true, value: execVar.value, // paramNames removed - they're already in executableDef and shouldn't be exposed as imports executableDef: execVar.internal?.executableDef, internal: serializedInternal }; } else if (variable.type === "template") { const templateVar = variable; moduleObject[name] = { __template: true, content: templateVar.value, templateSyntax: templateVar.templateSyntax, parameters: templateVar.parameters, templateAst: templateVar.internal?.templateAst || (Array.isArray(templateVar.value) ? templateVar.value : void 0) }; } else if (variable.type === "object" && typeof variable.value === "object" && variable.value !== null) { const resolvedObject = this.objectResolver.resolveObjectReferences(variable.value, childVars, { resolveStrings: options?.resolveStrings }); moduleObject[name] = resolvedObject; } else { moduleObject[name] = variable.value; } const descriptor = variable.mx ? varMxToSecurityDescriptor(variable.mx) : void 0; const mergedDescriptor = descriptor && envDescriptor ? mergeDescriptors(descriptor, envDescriptor) : descriptor ?? envDescriptor; const metadataForSerialization = {}; if (mergedDescriptor) { metadataForSerialization.security = mergedDescriptor; } if (variable.internal?.capability) { metadataForSerialization.capability = variable.internal.capability; } const serializedMetadata = VariableMetadataUtils.serializeSecurityMetadata(metadataForSerialization); if (serializedMetadata) { serializedMetadataMap[name] = serializedMetadata; } } if (Object.keys(serializedMetadataMap).length > 0) { moduleObject.__metadata__ = serializedMetadataMap; } const guards = guardNames.length > 0 && childEnv ? childEnv.serializeGuardsByNames(guardNames) : []; return { moduleObject, frontmatter, guards }; } /** * Create a variable from an imported value, inferring the type */ createVariableFromValue(name, value, importPath, originalName, options) { const source = { directive: "var", syntax: Array.isArray(value) ? "array" : value && typeof value === "object" ? "object" : "quoted", hasInterpolation: false, isMultiLine: false }; const deserialized = VariableMetadataUtils.deserializeSecurityMetadata(options?.serializedMetadata); const snapshot = options?.env?.getSecuritySnapshot?.(); let snapshotDescriptor = snapshot ? makeSecurityDescriptor({ labels: snapshot.labels, taint: snapshot.taint, sources: snapshot.sources, policyContext: snapshot.policy ? { ...snapshot.policy } : void 0 }) : void 0; let combinedDescriptor = deserialized.security; if (snapshotDescriptor) { combinedDescriptor = combinedDescriptor ? mergeDescriptors(combinedDescriptor, snapshotDescriptor) : snapshotDescriptor; } const baseMetadata = { isImported: true, importPath, originalName: originalName !== name ? originalName : void 0, definedAt: { line: 0, column: 0, filePath: importPath }, ...deserialized }; const initialMetadata = VariableMetadataUtils.applySecurityMetadata(baseMetadata, { labels: options?.securityLabels, existingDescriptor: combinedDescriptor }); const buildMetadata = /* @__PURE__ */ __name((extra) => VariableMetadataUtils.applySecurityMetadata({ ...initialMetadata, ...extra || {} }, { labels: options?.securityLabels, existingDescriptor: initialMetadata.security }), "buildMetadata"); if (isStructuredValue(value)) { return createStructuredValueVariable(name, value, source, buildMetadata({ isStructuredValue: true, structuredValueType: value.type })); } if (value && typeof value === "object" && "__executable" in value && value.__executable) { return this.createExecutableFromImport(name, value, source, buildMetadata(), options?.securityLabels); } if (value && typeof value === "object" && value.__template) { const templateSource = { directive: "var", syntax: "template", hasInterpolation: true, isMultiLine: true }; const tmplMetadata = buildMetadata(); const templateOptions = { metadata: tmplMetadata, internal: { templateAst: value.templateAst } }; return createTemplateVariable(name, value.content, value.parameters, value.templateSyntax === "tripleColon" ? "tripleColon" : "doubleColon", templateSource, templateOptions); } const originalType = this.inferVariableType(value); let processedValue = value; if (originalType === "array" && Array.isArray(processedValue)) { const isComplexArray = this.hasComplexContent(processedValue); if (process.env.MLLD_DEBUG_FIX === "true") { console.error("[VariableImporter] create array variable", { name, importPath, isComplexArray, sample: processedValue.slice(0, 2) }); } return createArrayVariable(name, processedValue, isComplexArray, source, buildMetadata({ isImported: true, importPath, originalName: originalName !== name ? originalName : void 0 })); } if (originalType === "object") { const normalizedObject = this.unwrapArraySnapshots(processedValue, importPath); const isComplex = this.hasComplexContent(normalizedObject); if (process.env.MLLD_DEBUG_FIX === "true") { console.error("[VariableImporter] create object variable", { name, importPath, isComplex, keys: Object.keys(normalizedObject || {}).slice(0, 5), agentRosterPreview: normalizedObject && normalizedObject.agent_roster }); try { fs.appendFileSync("/tmp/mlld-debug.log", JSON.stringify({ source: "VariableImporter", name, importPath, isComplex, keys: Object.keys(normalizedObject || {}).slice(0, 5), agentRosterType: normalizedObject && typeof normalizedObject.agent_roster, agentRosterIsVariable: this.isVariableLike(normalizedObject.agent_roster), agentRosterIsArray: Array.isArray(normalizedObject.agent_roster) }) + "\n"); } catch { } } return createObjectVariable(name, normalizedObject, isComplex, source, buildMetadata({ isImported: true, importPath, originalName: originalName !== name ? originalName : void 0 })); } return createImportedVariable(name, processedValue, originalType, importPath, false, originalName || name, source, buildMetadata()); } unwrapArraySnapshots(value, importPath) { if (Array.isArray(value)) { return value.map((item) => this.unwrapArraySnapshots(item, importPath)); } if (value && typeof value === "object") { if (value.__arraySnapshot) { const snapshot = value; const source = { directive: "var", syntax: "array", hasInterpolation: false, isMultiLine: false }; const arrayMetadata = { ...snapshot.metadata || {}, isImported: true, importPath, originalName: snapshot.name }; const normalizedElements = Array.isArray(snapshot.value) ? snapshot.value.map((item) => this.unwrapArraySnapshots(item, importPath)) : []; const arrayName = snapshot.name || "imported_array"; return createArrayVariable(arrayName, normalizedElements, snapshot.isComplex === true, source, arrayMetadata); } if (value.__executable) { const source = { directive: "exe", syntax: "braces", hasInterpolation: false, isMultiLine: false }; return this.createExecutableFromImport("property", value, source, { isImported: true, importPath }); } const result = {}; for (const [key, entry] of Object.entries(value)) { result[key] = this.unwrapArraySnapshots(entry, importPath); } return result; } return value; } /** * Create a namespace variable for imports with aliased wildcards (e.g., * as @config) */ createNamespaceVariable(alias, moduleObject, importPath, securityLabels, metadataMap, env) { const source = { directive: "var", syntax: "object", hasInterpolation: false, isMultiLine: false }; const isComplex = this.hasComplexContent(moduleObject); const snapshot = env?.getSecuritySnapshot?.(); let snapshotDescriptor = snapshot ? makeSecurityDescriptor({ labels: snapshot.labels, taint: snapshot.taint, sources: snapshot.sources, policyContext: snapshot.policy ? { ...snapshot.policy } : void 0 }) : void 0; const metadata = VariableMetadataUtils.applySecurityMetadata({ isImported: true, importPath, definedAt: { line: 0, column: 0, filePath: importPath }, namespaceMetadata: metadataMap }, { labels: securityLabels, existingDescriptor: snapshotDescriptor }); const namespaceOptions = { metadata, internal: { isNamespace: true } }; return createObjectVariable(alias, moduleObject, isComplex, source, namespaceOptions); } extractMetadataMap(moduleObject) { const container = moduleObject.__metadata__; if (!container || typeof container !== "object") { return void 0; } const result = {}; for (const [key, value] of Object.entries(container)) { result[key] = value; } return result; } /** * Merge variables into the target environment based on import type */ async handleImportType(directive, moduleObject, targetEnv, childEnv, metadataMap, guardDefinitions) { if (directive.subtype === "importPolicy") { await this.handleNamespaceImport(directive, moduleObject, targetEnv, childEnv, metadataMap, guardDefinitions); } else if (directive.subtype === "importAll") { throw new MlldImportError(`Wildcard imports '/import { * }' are no longer supported. Use namespace imports instead: '/import "file"' or '/import "file" as @name'`, directive.location, { suggestion: `Change '/import { * } from "file"' to '/import "file"'` }); } else if (directive.subtype === "importNamespace") { await this.handleNamespaceImport(directive, moduleObject, targetEnv, childEnv, metadataMap, guardDefinitions); } else if (directive.subtype === "importSelected") { if (guardDefinitions && guardDefinitions.length > 0) { targetEnv.registerSerializedGuards(guardDefinitions); } await this.handleSelectedImport(directive, moduleObject, targetEnv, childEnv, metadataMap); } else { throw new Error(`Unknown import subtype: ${directive.subtype}`); } } /** * Handle namespace imports */ async handleNamespaceImport(directive, moduleObject, targetEnv, childEnv, metadataMap, guardDefinitions) { const namespaceNodes = directive.values?.namespace; const alias = namespaceNodes && Array.isArray(namespaceNodes) && namespaceNodes[0]?.content ? namespaceNodes[0].content : directive.values?.imports?.[0]?.alias; if (!alias) { throw new Error("Namespace import missing alias"); } const importerFilePath = targetEnv.getCurrentFilePath(); const aliasLocationNode = namespaceNodes && Array.isArray(namespaceNodes) ? namespaceNodes[0] : void 0; const aliasLocation = aliasLocationNode?.location ? astLocationToSourceLocation(aliasLocationNode.location, importerFilePath) : astLocationToSourceLocation(directive.location, importerFilePath); const namespaceObject = moduleObject; const importPath = childEnv.getCurrentFilePath() || "unknown"; const importDisplay = this.getImportDisplayPath(directive, importPath); const bindingInfo = { source: importDisplay, location: aliasLocation }; this.ensureImportBindingAvailable(targetEnv, alias, importDisplay, aliasLocation); if (namespaceObject && typeof namespaceObject === "object" && namespaceObject.__template) { const templateVar = this.createVariableFromValue(alias, namespaceObject, importPath, void 0, { env: targetEnv }); this.setVariableWithImportBinding(targetEnv, alias, templateVar, bindingInfo); if (guardDefinitions && guardDefinitions.length > 0) { targetEnv.registerSerializedGuards(guardDefinitions); } if (directive.subtype === "importPolicy") { targetEnv.recordPolicyConfig(alias, namespaceObject); } return; } const securityLabels = directive.meta?.securityLabels || directive.values?.securityLabels; const namespaceVar = this.createNamespaceVariable(alias, namespaceObject, importPath, securityLabels, metadataMap, targetEnv); this.setVariableWithImportBinding(targetEnv, alias, namespaceVar, bindingInfo); if (guardDefinitions && guardDefinitions.length > 0) { targetEnv.registerSerializedGuards(guardDefinitions); } if (directive.subtype === "importPolicy") { const policyConfig = namespaceObject?.config ?? namespaceObject; targetEnv.recordPolicyConfig(alias, policyConfig); } } /** * Handle selected imports */ async handleSelectedImport(directive, moduleObject, targetEnv, childEnv, metadataMap) { const imports = directive.values?.imports || []; const importPath = childEnv.getCurrentFilePath() || "unknown"; const importDisplay = this.getImportDisplayPath(directive, importPath); const importerFilePath = targetEnv.getCurrentFilePath(); const securityLabels = directive.meta?.securityLabels || directive.values?.securityLabels; for (const importItem of imports) { const importName = importItem.identifier; const alias = importItem.alias || importName; if (!(importName in moduleObject)) { throw new Error(`Import '${importName}' not found in module`); } const bindingLocation = importItem?.location ? astLocationToSourceLocation(importItem.location, importerFilePath) : astLocationToSourceLocation(directive.location, importerFilePath); const bindingInfo = { source: importDisplay, location: bindingLocation }; this.ensureImportBindingAvailable(targetEnv, alias, importDisplay, bindingLocation); const importedValue = moduleObject[importName]; const serializedMetadata = metadataMap ? metadataMap[importName] : void 0; const variable = this.createVariableFromValue(alias, importedValue, importPath, importName, { securityLabels, serializedMetadata, env: targetEnv }); this.setVariableWithImportBinding(targetEnv, alias, variable, bindingInfo); } } /** * Produces a human-readable source string for error messages, stripping any * quotes that appeared in the original directive. */ getImportDisplayPath(directive, fallback) { const raw = directive?.raw; if (raw && typeof raw.path === "string" && raw.path.trim().length > 0) { const trimmed = raw.path.trim(); return trimmed.replace(/^['"]|['"]$/g, ""); } return fallback; } /** * Create an executable variable from import metadata */ createExecutableFromImport(name, value, source, metadata, securityLabels) { value.value; const executableDef = value.executableDef; const paramNames = executableDef?.paramNames || []; let originalInternal = value.internal || value.metadata || {}; if (originalInternal.capturedShadowEnvs) { originalInternal = { ...originalInternal, capturedShadowEnvs: this.deserializeShadowEnvs(originalInternal.capturedShadowEnvs) }; } if (originalInternal.capturedModuleEnv) { const deserializedEnv = this.deserializeModuleEnv(originalInternal.capturedModuleEnv); for (const [_, variable] of deserializedEnv) { if (variable.type === "executable") { const existingEnv = variable.internal?.capturedModuleEnv; if (!existingEnv || !(existingEnv instanceof Map)) { variable.internal = { ...variable.internal ?? {}, capturedModuleEnv: deserializedEnv }; } } } originalInternal = { ...originalInternal, capturedModuleEnv: deserializedEnv }; } const enhancedMetadata = { ...metadata, isImported: true, importPath: metadata.importPath }; const finalMetadata = VariableMetadataUtils.applySecurityMetadata(enhancedMetadata, { labels: securityLabels, existingDescriptor: enhancedMetadata.security }); const finalInternal = { ...originalInternal, executableDef }; const execVariable = createExecutableVariable(name, "command", "", paramNames, void 0, source, { metadata: finalMetadata, internal: finalInternal }); return execVariable; } /** * Check if a value contains complex AST nodes that need evaluation */ hasComplexContent(value) { if (value === null || typeof value !== "object") { return false; } if (this.isVariableLike(value)) { return false; } if (value.type) { return true; } if (value.__executable) { return true; } if (Array.isArray(value)) { return value.some((item) => this.hasComplexContent(item)); } for (const prop of Object.values(value)) { if (this.hasComplexContent(prop)) { return true; } } return false; } isVariableLike(value) { return value && typeof value === "object" && typeof value.type === "string" && "name" in value && "value" in value && "source" in value && "createdAt" in value && "modifiedAt" in value; } /** * Infer variable type from value */ inferVariableType(value) { if (isStructuredValue(value)) { return "structured"; } else if (Array.isArray(value)) { return "array"; } else if (value && typeof value === "object") { return "object"; } else if (typeof value === "string") { return "simple-text"; } else { return "simple-text"; } } /** * Check if a variable is a legitimate mlld variable that can be exported/imported. * System variables (tracked via internal.isSystem) are excluded * to prevent namespace collisions when importing multiple modules with system variables like @fm. */ isLegitimateVariableForExport(variable) { const isSystem = variable.internal?.isSystem ?? false; if (isSystem) { return false; } return true; } }; __name(_VariableImporter, "VariableImporter"); var VariableImporter = _VariableImporter; export { VariableImporter }; //# sourceMappingURL=chunk-WVAX2Q4I.mjs.map //# sourceMappingURL=chunk-WVAX2Q4I.mjs.map