"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.doTrusysSecurityTest = doTrusysSecurityTest;
exports.trusysRedteamRunCommand = trusysRedteamRunCommand;
const chalk_1 = __importDefault(require("chalk"));
const dedent_1 = __importDefault(require("dedent"));
const fs = __importStar(require("fs"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const lodash_1 = __importDefault(require("lodash"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const zod_1 = require("zod");
const cache_1 = require("../cache");
const envars_1 = require("../envars");
const evaluator_1 = require("../evaluator");
const evaluatorHelpers_1 = require("../evaluatorHelpers");
const accounts_1 = require("../globalConfig/accounts");
const cloud_1 = require("../globalConfig/cloud");
const logger_1 = __importDefault(require("../logger"));
const providers_1 = require("../providers");
const poison_1 = require("../redteam/commands/poison");
const sharedFrontend_1 = require("../redteam/sharedFrontend");
const telemetry_1 = __importDefault(require("../telemetry"));
const util_1 = require("../util");
const buildProviderForEval_1 = require("../util/customAIApplication/buildProviderForEval");
const customAICliHelpers_1 = require("../util/customAIApplication/customAICliHelpers");
const callServer_1 = require("./callServer");
async function doTrusysSecurityTest(cmdObj) {
    (0, util_1.setupEnv)(cmdObj.envPath);
    const apiKey = cloud_1.cloudConfig.getApiKey() || (0, envars_1.getEnvString)('TRUSYS_API_KEY');
    const appUrl = cloud_1.cloudConfig.getTrusysAppUrl();
    const projectId = (0, accounts_1.getProjectId)();
    const workSpaceId = (0, accounts_1.getWorkSpaceId)();
    if (!projectId || !apiKey) {
        await cloud_1.cloudConfig.delete();
        logger_1.default.info(`❌ Not logged in. Run ${chalk_1.default.bold('trusys auth login')} to login.`);
        process.exitCode = 1;
        return null;
    }
    const apiHost = cloud_1.cloudConfig.getTrusysApiHost();
    //const apiHost = 'http://localhost:15500';
    const securityConfigName = cmdObj?.securityConfig;
    const securityConfigDetails = await (0, callServer_1.getSecurityConfigDetails)(securityConfigName, apiKey, apiHost, projectId);
    if (securityConfigDetails?.projectId !== projectId) {
        logger_1.default.error(`❌ Security config ${securityConfigName} does not belong to project ${projectId}`);
        process.exitCode = 1;
        return null;
    }
    logger_1.default.info(`SecurityConfig: ${securityConfigDetails?.name}`);
    const app = await (0, callServer_1.getApplicationDetils)(securityConfigDetails?.config?.target?.label, apiKey, apiHost, projectId);
    if (lodash_1.default.isEmpty(app)) {
        logger_1.default.error(`❌ Application not found`);
        process.exitCode = 1;
        return null;
    }
    const customAIUpload = (0, customAICliHelpers_1.isCustomAIUploadMode)(app);
    const useShellCustomAIPath = (0, customAICliHelpers_1.requiresCliApplicationPath)(app);
    if (customAIUpload && (cmdObj?.applicationPath || cmdObj?.local)) {
        logger_1.default.warn(`Application "${app?.name}" uses upload-mode Custom AI; --applicationPath and --local are ignored. Running on the server.`);
    }
    const testSuite = {
        config: (0, sharedFrontend_1.getUnifiedConfig)(securityConfigDetails?.config, useShellCustomAIPath ? cmdObj?.applicationPath : undefined),
        force: true,
        verbose: false,
        delay: cmdObj?.delay ?? 0,
        maxConcurrency: cmdObj?.maxConcurrency ?? 20,
        applicationIds: [app?.id],
        securityConfigId: securityConfigDetails?.id,
        user: { email: 'test@test.com' },
        isCustomProvider: false,
    };
    if (customAIUpload) {
        const providerFromApp = (0, buildProviderForEval_1.buildCustomAIProviderForEval)(app);
        const target = testSuite?.config?.targets?.[0];
        if (providerFromApp && target) {
            testSuite.config.targets[0] = {
                ...target,
                id: providerFromApp.id,
                label: providerFromApp.label ?? target.label,
                applicationId: providerFromApp.applicationId,
                enableEncryption: providerFromApp.enableEncryption,
                config: {
                    ...target.config,
                    ...providerFromApp.config,
                },
            };
        }
    }
    const targetProviderId = typeof testSuite?.config?.targets?.[0]?.id === 'function'
        ? testSuite.config.targets[0].id()
        : (testSuite?.config?.targets?.[0]?.id ?? '');
    const useLocalCustomProvider = targetProviderId.startsWith('file://') ||
        (Boolean(cmdObj?.local) && targetProviderId !== buildProviderForEval_1.AZURE_SESSIONS_CUSTOM_AI_PROVIDER_ID);
    if (useShellCustomAIPath || (cmdObj?.local && !customAIUpload)) {
        if (useShellCustomAIPath && lodash_1.default.isEmpty(cmdObj?.applicationPath) && !cmdObj?.local) {
            logger_1.default.error(`❌ Please provide --applicationPath for shell-mode Custom AI application "${app?.name}".`);
            process.exitCode = 1;
            return null;
        }
        if (useShellCustomAIPath && !lodash_1.default.isEmpty(cmdObj?.applicationPath)) {
            const language = app?.provider_config?.config?.provider?.language;
            const applicationPath = String(cmdObj?.applicationPath);
            let hasCorrectExt = false;
            if (language === 'go') {
                hasCorrectExt = applicationPath.endsWith('.go');
            }
            else if (language === 'python') {
                hasCorrectExt = applicationPath.endsWith('.py');
            }
            else if (language === 'js') {
                hasCorrectExt =
                    applicationPath.endsWith('.js') ||
                        applicationPath.endsWith('.ts') ||
                        applicationPath.endsWith('.mjs');
            }
            else {
                logger_1.default.error(`❌ This application does not have any language config`);
                process.exitCode = 1;
                return null;
            }
            if (!hasCorrectExt) {
                logger_1.default.error(`❌ Please give ${language} file path`);
                process.exitCode = 1;
                return null;
            }
        }
        if (useLocalCustomProvider) {
            let configPath = cmdObj.config ?? 'trusysconfig.yaml';
            if (testSuite?.config) {
                const filename = `redteam-${Date.now()}.yaml`;
                const tmpDir = cmdObj.loadedFromCloud ? '' : os.tmpdir();
                const tmpFile = path.join(tmpDir, filename);
                fs.mkdirSync(path.dirname(tmpFile), { recursive: true });
                fs.writeFileSync(tmpFile, js_yaml_1.default.dump(testSuite?.config));
                configPath = tmpFile;
            }
            const remoteGen = await (0, callServer_1.generateRedteamConfigTrusysRemote)(fs.readFileSync(configPath, 'utf8'), cmdObj.liveRedteamConfig?.commandLineOptions || {}, apiKey, apiHost, projectId);
            const redteamConfig = remoteGen?.data ?? remoteGen;
            const providerId = typeof testSuite?.config?.targets[0]?.id === 'function'
                ? testSuite?.config.targets[0].id()
                : (testSuite?.config?.targets[0]?.id ?? '');
            const providerClass = await (0, providers_1.loadApiProvider)(providerId, {
                basePath: testSuite?.config?.targets[0]?.config?.basePath,
                env: testSuite?.config?.targets[0]?.config?.env,
                options: { config: { ...(testSuite?.config?.targets[0]?.config ?? {}) } },
            });
            await Promise.all((redteamConfig?.tests ?? [])?.map(async (test) => {
                const prompt = {
                    raw: Array.isArray(redteamConfig?.prompts) ? redteamConfig?.prompts[0] : '',
                    label: Array.isArray(redteamConfig?.prompts)
                        ? redteamConfig?.prompts[0]
                        : '',
                };
                const renderedPrompt = await (0, evaluatorHelpers_1.renderPrompt)(prompt, test?.vars);
                const providerResponse = await providerClass.callApi(renderedPrompt);
                test.providerResponse = providerResponse;
                test.providerOutput = providerResponse.output;
                return providerResponse;
            }));
            testSuite.config = redteamConfig;
            testSuite.isCustomProvider = true;
        }
    }
    const result = await (0, callServer_1.securityTestRunHandler)(testSuite, apiHost, apiKey, projectId);
    if (!result?.success) {
        logger_1.default.error(`Test suite failed: ${result?.error}`);
        process.exitCode = 1;
        return null;
    }
    logger_1.default.info(`✅ Security run completed`);
    const shareUrl = `${appUrl}/workspaces/${workSpaceId}/projects/${projectId}/redteam-test-result/${result?.testRunId}`;
    logger_1.default.info(chalk_1.default.green(`✅ Test run completed`));
    logger_1.default.info(chalk_1.default.blue(`Eval test completed: ${shareUrl}`));
    const trusysTestResult = await (0, callServer_1.getTestRunDetails)(result?.testRunId, apiKey, apiHost, projectId);
    const failedTestCount = trusysTestResult?.data?.summary?.stats?.failures;
    if (failedTestCount && failedTestCount > 0) {
        process.exitCode = 100;
    }
    if (cmdObj.output) {
        await (0, util_1.writeTrusysOutput)(cmdObj.output, trusysTestResult?.data, shareUrl, true);
        logger_1.default.info(chalk_1.default.blue(`Security Run ${result?.testRunId} has been successfully exported to ${cmdObj.output}.`));
    }
    return null;
}
function trusysRedteamRunCommand(program) {
    program
        .command('run')
        .description((0, dedent_1.default) `
        ${chalk_1.default.red('Red team')} a target application, a two-step process:

        1. Generates dynamic attack probes (i.e. test cases) tailored to your target application using specialized uncensored models.
        2. Evaluates the generated probes against your target application.
      `)
        .option('-j, --max-concurrency <number>', 'Maximum number of concurrent API calls', (val) => Number.parseInt(val, 10), evaluator_1.DEFAULT_MAX_CONCURRENCY)
        .option('--delay <number>', 'Delay in milliseconds between API calls', (val) => Number.parseInt(val, 10))
        .option('--force', 'Force generation even if no changes are detected', false)
        .option('--securityConfig <securityConfig>', 'Security Config to use')
        // .option('--user <user>', 'Email Id for Security Run')
        .option('--applicationPath <path>', 'Path to custom provider file')
        .option('--local', 'Run evaluation locally using application file')
        .option('-o, --output <outputPath>', 'Output path for the exported file')
        .option('--cache', 'Enable caching for API requests')
        .option('--noCache', 'Disable caching for API requests')
        .action(async (opts) => {
        (0, util_1.setupEnv)(opts.envPath);
        if (opts.cache) {
            (0, cache_1.enableCache)();
        }
        else if (opts.noCache) {
            (0, cache_1.disableCache)();
        }
        else {
            (0, cache_1.disableCache)();
        }
        telemetry_1.default.record('command_used', {
            name: 'redteam run',
        });
        await telemetry_1.default.send();
        try {
            if (opts.securityConfig) {
                await doTrusysSecurityTest(opts);
                return;
            }
        }
        catch (error) {
            if (error instanceof zod_1.z.ZodError) {
                logger_1.default.error('Invalid options:');
                error.errors.forEach((err) => {
                    logger_1.default.error(`  ${err.path.join('.')}: ${err.message}`);
                });
            }
            else {
                logger_1.default.error(`An unexpected error occurred during red team run: ${error instanceof Error ? error.message : String(error)}\n${error instanceof Error ? error.stack : ''}`);
            }
            process.exitCode = 1;
        }
    });
    (0, poison_1.poisonCommand)(program);
}
//# sourceMappingURL=trusys_redteam.js.map