/**
 * API Executor Service - Handles API execution with enhanced error handling.
 *
 * This service provides a wrapper around aiExecuteV2Api with proto validation
 * and comprehensive error reporting for the AI model.
 */
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
import { Api as ProtoApi } from "@superblocksteam/types";
import { safeJsonStringify } from "../../../../util/log-sanitizer.js";
import { getLogger, getErrorMeta } from "../../../../util/logger.js";
import llmobs from "../../../llmobs/index.js";
import { validateBlockFieldNames } from "./build-api-artifact.js";
let ApiExecutor = (() => {
    let _instanceExtraInitializers = [];
    let _execute_decorators;
    return class ApiExecutor {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
            _execute_decorators = [llmobs.decorate({ name: "ApiExecutor.execute", kind: "task" })];
            __esDecorate(this, null, _execute_decorators, { kind: "method", name: "execute", static: false, private: false, access: { has: obj => "execute" in obj, get: obj => obj.execute }, metadata: _metadata }, null, _instanceExtraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        editorClient = __runInitializers(this, _instanceExtraInitializers);
        integrationStore;
        constructor(editorClient, integrationStore) {
            this.editorClient = editorClient;
            this.integrationStore = integrationStore;
        }
        /**
         * Execute an API with comprehensive error handling.
         *
         * @param api - The API definition to execute
         * @param inputs - Input variables for the API
         * @param options - Execution options
         * @returns Execution result with outputs or detailed error information
         */
        async execute(api, inputs = {}, options = {}) {
            const logger = getLogger();
            const startTime = Date.now();
            const apiName = api.type === "fetch" ? api.apiName : api.definition.metadata.name;
            logger.info(`Executing API ${apiName}`, api.type === "inline" ? safeJsonStringify(api.definition, 2) : "");
            try {
                // Validate proto compatibility before execution
                if (api.type === "inline") {
                    this.validateProtoCompatibility(api.definition);
                }
                // Step 3: Execute via aiExecuteV2Api
                const result = await this.editorClient.call.aiExecuteV2Api({
                    api,
                    inputs,
                    includeStepOutputs: options.includeStepOutputs,
                    timeout: options.timeout,
                });
                let errors = result.errors?.map((err) => ({
                    ...this.parseExecutionError(err.message),
                    handled: err.handled,
                }));
                const topLevelError = result.systemError;
                if (!errors?.length && typeof topLevelError === "string") {
                    errors = [this.parseExecutionError(topLevelError)];
                }
                const success = !result.systemError &&
                    (!errors || errors.length === 0 || errors.every((err) => err.handled));
                return {
                    success,
                    outputs: result.outputs,
                    stepOutputs: result.stepOutputs,
                    executionTime: Date.now() - startTime,
                    systemError: result.systemError,
                    errors,
                };
            }
            catch (error) {
                logger.error(`API execution failed for ${apiName}:`, getErrorMeta(error));
                let errorMessage;
                if (error instanceof Error) {
                    errorMessage = error.message;
                }
                else if (typeof error === "object" &&
                    error !== null &&
                    "message" in error) {
                    errorMessage = String(error.message);
                }
                else {
                    errorMessage = String(error);
                }
                const parsedError = this.parseExecutionError(errorMessage);
                return {
                    success: false,
                    errors: [
                        {
                            ...parsedError,
                            handled: false,
                            originalError: error,
                        },
                    ],
                    executionTime: Date.now() - startTime,
                };
            }
        }
        /**
         * Validate that the API can roundtrip through protobuf.
         * Throws an error if validation fails.
         */
        validateProtoCompatibility(api) {
            const logger = getLogger();
            // Check for incorrect generic field names
            validateBlockFieldNames(api, this.integrationStore);
            try {
                // Convert to proto and back
                const proto = ProtoApi.fromJson(api);
                const roundtripped = proto.toJson();
                // Basic validation that critical fields survived
                if (!roundtripped.metadata?.name) {
                    throw new Error("Critical metadata fields lost in proto conversion");
                }
                logger.debug(`Proto validation passed for API ${api.metadata.name}`);
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                logger.error(`Proto validation failed for API ${api.metadata.name}: ${message}`);
                throw new Error(`Proto validation failed: ${message}\n` +
                    `Common issues:\n` +
                    `- Boolean fields: Omit false values instead of including them\n` +
                    `- Enum values: Use exact string values from the schema\n` +
                    `- Required fields: Ensure all required fields are present\n` +
                    `- Field types: Ensure values match expected types`);
            }
        }
        /**
         * Parse error messages to extract the message and body (JSON) separately.
         *
         * @param errorMessage - The raw error message that may contain JSON with escaped newlines
         * @returns Object with message and body if body exists, undefined otherwise
         */
        parseErrorMessage(errorMessage) {
            // Check if the message contains a Body section with JSON
            // Look for Body: followed by JSON, handling escaped newlines
            const bodyIndex = errorMessage.indexOf("Body:");
            if (bodyIndex !== -1) {
                // Find the JSON part after Body:
                const afterBody = errorMessage.substring(bodyIndex + 5); // Skip "Body:"
                const jsonMatch = afterBody.match(/(\{[\s\S]*\})/);
                if (jsonMatch && jsonMatch[1]) {
                    try {
                        // Extract and clean the JSON string
                        const jsonString = jsonMatch[1]
                            .replace(/^\\n/, "") // Remove leading \n if present
                            .replace(/\\n/g, "\n")
                            .replace(/\\"/g, '"')
                            .replace(/\\\\/g, "\\");
                        // Parse and re-stringify with proper formatting
                        const parsed = JSON.parse(jsonString);
                        const prettyJson = JSON.stringify(parsed, null, 2);
                        // Get the part before Body: and remove trailing \n
                        let messageText = errorMessage.substring(0, bodyIndex);
                        if (messageText.endsWith("\\n")) {
                            messageText = messageText.slice(0, -2);
                        }
                        return {
                            message: messageText,
                            body: prettyJson,
                        };
                    }
                    catch {
                        // If parsing fails, return undefined to indicate no parseable body
                        return undefined;
                    }
                }
            }
            // If no Body section found, try to detect standalone JSON
            const jsonMatch = errorMessage.match(/^(.*?)(\{[\s\S]*\})$/);
            if (jsonMatch && jsonMatch[1] !== undefined && jsonMatch[2]) {
                try {
                    const jsonString = jsonMatch[2]
                        .replace(/\\n/g, "\n")
                        .replace(/\\"/g, '"')
                        .replace(/\\\\/g, "\\");
                    const parsed = JSON.parse(jsonString);
                    const prettyJson = JSON.stringify(parsed, null, 2);
                    // Get the non-JSON part as message
                    const messageText = jsonMatch[1].trim().replace(/\\n/g, "\n");
                    return {
                        message: messageText || "Error",
                        body: prettyJson,
                    };
                }
                catch {
                    // If parsing fails, return undefined
                    return undefined;
                }
            }
            // No parseable body found
            return undefined;
        }
        /**
         * Parse execution errors to provide helpful details.
         */
        parseExecutionError(errorMessage) {
            // Parse the error message to extract message and body
            const parsedError = this.parseErrorMessage(errorMessage);
            // Build messages for different purposes
            const fullMessage = parsedError
                ? `${parsedError.message}\n\nBody:\n\n${parsedError.body}`
                : errorMessage.replace(/\\n/g, "\n");
            const displayMessage = parsedError
                ? parsedError.message
                : errorMessage.replace(/\\n/g, "\n");
            const bodyDetails = parsedError?.body;
            // Check for common error patterns
            if (errorMessage.includes("Proto")) {
                return {
                    type: "proto",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: this.getProtoErrorSuggestion(errorMessage),
                };
            }
            if (errorMessage.includes("ValidationError")) {
                return {
                    type: "validation",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: this.getValidationErrorSuggestion(errorMessage),
                };
            }
            if (errorMessage.includes("ReferenceError")) {
                const match = errorMessage.match(/ReferenceError: (\w+) is not defined/);
                const variable = match?.[1];
                return {
                    type: "binding",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: variable
                        ? `Variable "${variable}" is not defined. ` +
                            `If you're using ${variable}.value or ${variable}.set() in a Loop or JavaScript block, ` +
                            `you must define "${variable}" in a Variables block BEFORE the block that uses it. ` +
                            `Example: new Variables("state", [{ key: "${variable}", value: () => initialValue }])`
                        : "A variable is not defined. Check that all inputs are supplied and that variables used with .value/.set() are defined in a preceding Variables block.",
                };
            }
            // Check for undefined property access - often indicates missing Variables block
            // Modern JS: "Cannot read properties of undefined (reading 'value')"
            // Older JS: "Cannot read property 'value' of undefined"
            if (errorMessage.includes("Cannot read") &&
                (errorMessage.includes("undefined") || errorMessage.includes("null"))) {
                // Try modern format first: Cannot read properties of undefined (reading 'value')
                let propMatch = errorMessage.match(/Cannot read properties of (?:undefined|null) \(reading ['"](\w+)['"]\)/);
                // Fallback to older format: Cannot read property 'value' of undefined
                if (!propMatch) {
                    propMatch = errorMessage.match(/Cannot read property ['"](\w+)['"] of (?:undefined|null)/);
                }
                const property = propMatch?.[1];
                return {
                    type: "binding",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: property
                        ? `Attempted to access "${property}" on an undefined value. ` +
                            `This often happens when using .value or .set() on a variable that isn't defined in a Variables block. ` +
                            `Ensure all variables used with .value/.set() are declared in a preceding Variables block.`
                        : "Attempted to access a property on undefined. Check that all variables are properly defined before use.",
                };
            }
            if (errorMessage.includes("Integration") &&
                errorMessage.includes("not found")) {
                return {
                    type: "validation",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: "Check that the integration ID exists and is accessible. Use listIntegrations to see available integrations.",
                };
            }
            if (errorMessage.includes("UUID")) {
                return {
                    type: "validation",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: 'Organization ID must be a valid UUID. Use "00000000-0000-0000-0000-000000000000" for testing.',
                };
            }
            if (errorMessage.includes("type must not be in list [0]")) {
                return {
                    type: "validation",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: "Variables block 'type' field must be a non-zero value. Use 1 for TYPE_SIMPLE.",
                };
            }
            if (this.isAuthError(errorMessage)) {
                return {
                    type: "auth",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: "There may be an issue with the integration configuration. Verify the integration is set up correctly and the credentials are valid.",
                };
            }
            if (this.isRateLimitError(errorMessage)) {
                return {
                    type: "rate_limit",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: "The integration reported a rate limit. Wait a moment and retry or reduce the frequency of requests.",
                };
            }
            if (this.isBindingError(errorMessage)) {
                return {
                    type: "binding",
                    message: fullMessage,
                    displayMessage,
                    bodyDetails,
                    suggestion: "Review component and state bindings referenced by the API to ensure they exist and are spelled correctly.",
                };
            }
            // Generic execution error
            return {
                type: "execution",
                message: fullMessage,
                displayMessage,
                bodyDetails,
            };
        }
        /**
         * Determine whether an error message indicates an authentication or authorization failure.
         */
        isAuthError(message) {
            return /(unauthorized|forbidden|permission denied|401|403|oauth|no token|authentication|credential|token.*expired|invalid.*token)/i.test(message);
        }
        /**
         * Determine whether an error message indicates a rate limit condition.
         */
        isRateLimitError(message) {
            return /(429|rate limit|throttled)/i.test(message);
        }
        /**
         * Determine whether an error message indicates missing or incorrect bindings.
         */
        isBindingError(message) {
            return /ReferenceError|is not defined|Cannot read properties/i.test(message);
        }
        /**
         * Get suggestion for proto errors.
         */
        getProtoErrorSuggestion(errorMessage) {
            if (errorMessage.includes("boolean")) {
                return "Omit boolean fields that are false. Only include them when true.";
            }
            if (errorMessage.includes("enum")) {
                return "Check that enum values use the exact string constants from the schema.";
            }
            if (errorMessage.includes("required")) {
                return "Ensure all required fields are present: metadata.id, metadata.name, metadata.organization";
            }
            return "Review the API structure against the schema. Common issues: false booleans should be omitted, enums need exact values.";
        }
        /**
         * Get suggestion for validation errors.
         */
        getValidationErrorSuggestion(errorMessage) {
            const lowerMessage = errorMessage.toLowerCase();
            // Check for generic "ValidationError" with no useful details
            // Pattern: "Failed to execute X. Error: ValidationError" or just "ValidationError"
            const isGenericValidationError = /^(Failed to execute \w+\. )?(Error: )?ValidationError$/i.test(errorMessage.trim());
            if (isGenericValidationError) {
                return ("The API failed validation but no specific error details were provided. " +
                    "This typically indicates an issue with the API structure. Common causes:\n" +
                    "1) Using .value/.set() on variables not defined in a Variables block - " +
                    "if your code uses counter.value or results.set(), define them first: " +
                    "new Variables('state', [{ key: 'counter', value: () => 0 }])\n" +
                    "2) TYPE_WHILE loop conditions must use destructured parameters: " +
                    "condition: ({ counter }) => counter.value < 10 (not just counter.value < 10)\n" +
                    "3) Variable values in Variables blocks must be JavaScript expressions - " +
                    "strings need quotes: value: () => 'myString' not value: () => myString\n" +
                    "4) Control block structure issues - use getApiDocs() to verify correct syntax");
            }
            // Check for undefined variable patterns - common when using .value/.set() without Variables block
            const undefinedVarMatch = errorMessage.match(/(\w+) is not defined|Cannot read propert(?:y|ies) [^']* of undefined.*?(\w+)|(\w+)\.value|(\w+)\.set\(/);
            if (undefinedVarMatch) {
                const varName = undefinedVarMatch[1] ||
                    undefinedVarMatch[2] ||
                    undefinedVarMatch[3] ||
                    undefinedVarMatch[4];
                if (varName) {
                    return (`Variable "${varName}" is not defined. ` +
                        `If you're using ${varName}.value or ${varName}.set(), you must define "${varName}" in a Variables block BEFORE the Loop or block that uses it. ` +
                        `Example: new Variables("state", [{ key: "${varName}", value: () => initialValue }])`);
                }
            }
            // Check for .value/.set() usage patterns that suggest missing Variables block
            if (lowerMessage.includes(".value") ||
                lowerMessage.includes(".set(") ||
                lowerMessage.includes("cannot read") ||
                lowerMessage.includes("undefined")) {
                return ("This error often occurs when using .value or .set() on variables that aren't defined in a Variables block. " +
                    "If your Loop condition or JavaScript block references variables like 'counter.value' or 'results.set()', " +
                    "you MUST first create a Variables block that defines these variables. " +
                    "Example: new Variables('state', [{ key: 'counter', value: () => 0 }, { key: 'results', value: () => [] }])");
            }
            // Check for loop-related errors
            if (lowerMessage.includes("loop") ||
                lowerMessage.includes("while") ||
                lowerMessage.includes("condition")) {
                return ("Loop validation error. For TYPE_WHILE loops: " +
                    "1) The condition must be a function returning a boolean, e.g., ({ counter }) => counter.value < 10. " +
                    "2) Any variables used with .value/.set() must be defined in a preceding Variables block. " +
                    "3) While loops don't have an 'item' variable - only 'index' is available inside the loop.");
            }
            // Check for integration errors
            if (lowerMessage.includes("integration")) {
                return "Check that all integration IDs referenced in blocks actually exist and are accessible. Use listIntegrations to see available integrations.";
            }
            // Check for Variables block errors
            if (lowerMessage.includes("variables")) {
                return ("Variables block error. Remember: " +
                    "1) Variable values are JavaScript expressions - strings need quotes: value: () => 'myString'. " +
                    "2) Variables block must come BEFORE any blocks that reference those variables. " +
                    "3) Access variables with .value (read) and .set() (write).");
            }
            // Check for type/schema errors
            if (lowerMessage.includes("type") ||
                lowerMessage.includes("schema") ||
                lowerMessage.includes("invalid")) {
                return ("The API structure doesn't match the expected schema. Common issues: " +
                    "1) Loop type must be 'TYPE_FOREACH' or 'TYPE_WHILE'. " +
                    "2) Conditional blocks need 'if.condition' (not 'if.when'). " +
                    "3) Boolean fields should be omitted when false.");
            }
            // Generic fallback with actionable guidance
            return ("Validation failed. Common causes: " +
                "1) Using .value/.set() on variables not defined in a Variables block. " +
                "2) Referencing components or state that don't exist. " +
                "3) Invalid block structure or missing required fields. " +
                "Check the API source and ensure all referenced variables are properly defined.");
        }
    };
})();
export { ApiExecutor };
//# sourceMappingURL=api-executor.js.map