import { dirname, join, resolve } from 'path';
import { fileURLToPath } from 'url';
import { Deployer, FileService } from '@mastra/deployer';
import { copy, readJSON } from 'fs-extra/esm';
import os from 'os';
import * as fs from 'fs';
import { MastraError } from '@mastra/core/error';
import { Writable } from 'stream';
import { execa } from 'execa';
import { LoggerTransport } from '@mastra/core/logger';
import { PinoLogger } from '@mastra/loggers';
import { createClient } from 'redis';

// src/index.ts

// src/utils/auth.ts
function getAuthEntrypoint() {
  const tokensObject = {};
  if (process.env.PLAYGROUND_JWT_TOKEN) {
    tokensObject[process.env.PLAYGROUND_JWT_TOKEN] = { id: "business-api", role: "api" };
  }
  if (process.env.BUSINESS_JWT_TOKEN) {
    tokensObject[process.env.BUSINESS_JWT_TOKEN] = { id: "business-api", role: "api" };
  }
  return `
  import { SimpleAuth, CompositeAuth } from '@mastra/core/server';
  import { MastraCloudAuthProvider, MastraRBACCloud } from '@mastra/auth-cloud';

  // Service token auth (for business-api, playground internal calls)
  class MastraCloudServiceAuth extends SimpleAuth {
    constructor() {
      super({
        tokens: ${JSON.stringify(tokensObject)}
      });
    }

    async authorizeUser(user, request) {
      // Allow access to /api path
      if (request && request.url && new URL(request.url).pathname === '/api') {
        return true;
      }
      // Allow access for business-api users
      if (user && user.id === 'business-api') {
        return true;
      }
      return false;
    }
  }

  const serviceAuth = new MastraCloudServiceAuth();

  // Cloud user auth (for end users via OAuth)
  // Only enabled if MASTRA_CLOUD_API_URL is set
  let cloudUserAuth = null;
  if (process.env.MASTRA_CLOUD_API_URL) {
    cloudUserAuth = new MastraCloudAuthProvider({
      projectId: process.env.PROJECT_ID,
      cloudBaseUrl: process.env.MASTRA_CLOUD_API_URL,
      callbackUrl: process.env.MASTRA_CLOUD_CALLBACK_URL || \`\${process.env.MASTRA_CLOUD_API_URL}/auth/callback\`,
    });
  }

  const serverConfig = mastra.getServer();
  const userAuth = serverConfig?.auth;

  // Only enable auth if cloudUserAuth or userAuth are defined
  if (serverConfig && (cloudUserAuth || userAuth)) {
    // Build provider list: service auth first, then cloud user auth, then user's custom auth
    const providers = [serviceAuth];
    if (cloudUserAuth) {
      providers.push(cloudUserAuth);
    }
    if (userAuth) {
      providers.push(userAuth);
    }

    serverConfig.auth = new CompositeAuth(providers);

    // If cloud auth is enabled but no RBAC is configured, add default cloud RBAC
    if (cloudUserAuth && !serverConfig.rbac) {
      serverConfig.rbac = new MastraRBACCloud({
        roleMapping: {
          owner: ['*'],
          admin: ['*:read', '*:write', '*:execute'],
          api: ['*:read', '*:write', '*:execute'],
          member: ['*:read', '*:execute'],
          viewer: ['*:read'],
          _default: [],
        },
      });
    }
  }
  `;
}
var LOCAL = process.env.LOCAL === "true";
var TEAM_ID = process.env.TEAM_ID ?? "";
var PROJECT_ID = process.env.PROJECT_ID ?? "";
var BUILD_ID = process.env.BUILD_ID ?? "";
var BUILD_URL = process.env.BUILD_URL ?? "";
var LOG_REDIS_URL = process.env.LOG_REDIS_URL ?? "redis://localhost:6379";
var USER_IP_ADDRESS = process.env.USER_IP_ADDRESS ?? "";
var MASTRA_DIRECTORY = process.env.MASTRA_DIRECTORY ?? "src/mastra";
var PROJECT_ENV_VARS = safelyParseJson(process.env.PROJECT_ENV_VARS ?? "{}");
LOCAL ? os.tmpdir() + "/project" : process.env.PROJECT_ROOT ?? "/project";
function safelyParseJson(json) {
  try {
    return JSON.parse(json);
  } catch {
    return {};
  }
}
var redisClient = createClient({
  url: LOG_REDIS_URL
});
var RedisTransport = class extends LoggerTransport {
  _transform(chunk, _encoding, callback) {
    chunk = chunk.toString();
    const logKey = `builder:logs:${TEAM_ID}:${PROJECT_ID}:${BUILD_ID}`;
    const ttl = 2 * 24 * 60 * 60;
    const logData = typeof chunk === "string" ? chunk : JSON.stringify(chunk);
    process.nextTick(async () => {
      try {
        if (!redisClient.isOpen) {
          await redisClient.connect().catch((err) => {
            console.error("Redis connection error:", err);
          });
        }
        const pipeline = redisClient.multi();
        pipeline.rPush(logKey, logData);
        pipeline.expire(logKey, ttl);
        await pipeline.exec();
      } catch (err) {
        console.error("Redis logging error:", err);
      }
    });
    callback(null, chunk);
  }
  _write(chunk, encoding, callback) {
    if (typeof callback === "function") {
      this._transform(chunk, encoding || "utf8", callback);
      return true;
    }
    this._transform(chunk, encoding || "utf8", (error) => {
      if (error) console.error("Transform error in write:", error);
    });
    return true;
  }
  async _flush() {
    await new Promise((resolve2) => setTimeout(resolve2, 10));
  }
  async _destroy(err, cb) {
    await closeLogger();
    cb(err);
  }
  listLogs(_args) {
    return Promise.resolve({
      logs: [],
      total: 0,
      page: _args?.page ?? 1,
      perPage: _args?.perPage ?? 100,
      hasMore: false
    });
  }
  listLogsByRunId(_args) {
    return Promise.resolve({
      logs: [],
      total: 0,
      page: _args?.page ?? 1,
      perPage: _args?.perPage ?? 100,
      hasMore: false
    });
  }
};
var transport = new RedisTransport();
var closeLogger = async () => {
  if (redisClient.isOpen) {
    setTimeout(async () => {
      await redisClient.quit();
    }, 10);
  }
};
var logger = new PinoLogger({
  level: "info",
  transports: {
    redis: transport
  }
});

// src/utils/execa.ts
var createPinoStream = () => {
  return new Writable({
    write(chunk, _encoding, callback) {
      const line = chunk.toString().trim();
      if (line) {
        logger.info(line);
      }
      callback();
    }
  });
};
async function runWithExeca({
  cmd,
  args,
  cwd = process.cwd(),
  env = PROJECT_ENV_VARS
}) {
  const pinoStream = createPinoStream();
  try {
    const subprocess = execa(cmd, args, {
      cwd,
      stdin: "ignore",
      env: {
        ...process.env,
        ...env
      }
    });
    subprocess.stdout?.pipe(pinoStream, { end: false });
    subprocess.stderr?.pipe(pinoStream, { end: false });
    const { stdout, stderr, exitCode } = await subprocess;
    pinoStream.end();
    return { stdout, stderr, success: exitCode === 0 };
  } catch (error) {
    pinoStream.end();
    logger.error("Process failed", { error });
    return { success: false, error: error instanceof Error ? error : new Error(String(error)) };
  }
}

// src/utils/deps.ts
var MEMOIZED = /* @__PURE__ */ new Map();
function findLockFile(dir) {
  const lockFiles = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock"];
  for (const file of lockFiles) {
    if (fs.existsSync(join(dir, file))) {
      return file;
    }
  }
  const parentDir = resolve(dir, "..");
  if (parentDir !== dir) {
    return findLockFile(parentDir);
  }
  return null;
}
function detectPm({ path }) {
  const cached = MEMOIZED.get(path);
  if (cached) {
    return cached;
  }
  const lockFile = findLockFile(path);
  let pm = "npm";
  switch (lockFile) {
    case "pnpm-lock.yaml":
      pm = "pnpm";
      break;
    case "package-lock.json":
      pm = "npm";
      break;
    case "yarn.lock":
      pm = "yarn";
      break;
    case "bun.lock":
      pm = "bun";
      break;
    default:
      pm = "npm";
  }
  MEMOIZED.set(path, pm);
  return pm;
}
async function installDeps({ path, pm }) {
  pm = pm ?? detectPm({ path });
  logger.info("Installing dependencies", { pm, path });
  const args = ["install", "--legacy-peer-deps=false", "--force"];
  const { success, error } = await runWithExeca({ cmd: pm, args, cwd: path });
  if (!success) {
    throw new MastraError(
      {
        id: "FAIL_INSTALL_DEPS",
        category: "USER",
        domain: "DEPLOYER"
      },
      error
    );
  }
}
function getMastraEntryFile(mastraDir) {
  try {
    const fileService = new FileService();
    return fileService.getFirstExistingFile([
      join(mastraDir, MASTRA_DIRECTORY, "index.ts"),
      join(mastraDir, MASTRA_DIRECTORY, "index.js")
    ]);
  } catch (error) {
    throw new MastraError(
      {
        id: "MASTRA_ENTRY_FILE_NOT_FOUND",
        category: "USER",
        domain: "DEPLOYER"
      },
      error
    );
  }
}

// src/utils/report.ts
function successEntrypoint() {
  return `
  if (process.env.CI !== 'true') {
    await fetch('${process.env.REPORTER_API_URL}', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: 'Bearer ${process.env.REPORTER_API_URL_AUTH_TOKEN}',
      },
      body: JSON.stringify(${JSON.stringify({
    projectId: PROJECT_ID,
    buildId: BUILD_ID,
    status: "success",
    url: BUILD_URL,
    userIpAddress: USER_IP_ADDRESS
  })}),
    }).catch(err => {
      console.error('Failed to report build status to monitoring service', {
        error: err,
      });
    })
  }
  `;
}

// src/index.ts
var CloudDeployer = class extends Deployer {
  studio;
  constructor({ studio } = {}) {
    super({ name: "cloud" });
    this.studio = studio ?? false;
  }
  async getUserBundlerOptions(mastraEntryFile, outputDirectory) {
    const bundlerOptions = await super.getUserBundlerOptions(mastraEntryFile, outputDirectory);
    return {
      ...bundlerOptions,
      externals: true
    };
  }
  async deploy(_outputDirectory) {
  }
  async prepare(outputDirectory) {
    await super.prepare(outputDirectory);
    if (this.studio) {
      const __filename = fileURLToPath(import.meta.url);
      const __dirname = dirname(__filename);
      const studioServePath = join(outputDirectory, this.outputDir, "studio");
      await copy(join(dirname(__dirname), join("dist", "studio")), studioServePath, {
        overwrite: true
      });
    }
  }
  async writePackageJson(outputDirectory, dependencies) {
    const versions = await readJSON(join(dirname(fileURLToPath(import.meta.url)), "../versions.json"));
    for (const [pkgName, version] of Object.entries(versions || {})) {
      dependencies.set(pkgName, version);
    }
    return super.writePackageJson(outputDirectory, dependencies);
  }
  async lint() {
  }
  async installDependencies(outputDirectory, _rootDir = process.cwd()) {
    await installDeps({ path: join(outputDirectory, "output"), pm: "npm" });
  }
  async bundle(mastraDir, outputDirectory) {
    const currentCwd = process.cwd();
    process.chdir(mastraDir);
    const mastraEntryFile = getMastraEntryFile(mastraDir);
    const mastraAppDir = join(mastraDir, MASTRA_DIRECTORY);
    const discoveredTools = this.getAllToolPaths(mastraAppDir);
    await this.prepare(outputDirectory);
    await this._bundle(
      this.getEntry(),
      mastraEntryFile,
      {
        outputDirectory,
        projectRoot: mastraDir
      },
      discoveredTools
    );
    process.chdir(currentCwd);
  }
  getAuthEntrypoint() {
    return getAuthEntrypoint();
  }
  getEntry() {
    return `
import { createNodeServer, getToolExports } from '#server';
import { tools } from '#tools';
import { mastra } from '#mastra';
import { MultiLogger } from '@mastra/core/logger';
import { PinoLogger } from '@mastra/loggers';
import { HttpTransport } from '@mastra/loggers/http';
import { LibSQLStore, LibSQLVector } from '@mastra/libsql';
import { scoreTracesWorkflow } from '@mastra/core/evals/scoreTraces';

const startTime = process.env.RUNNER_START_TIME ? new Date(process.env.RUNNER_START_TIME).getTime() : Date.now();
const createNodeServerStartTime = Date.now();

console.log(JSON.stringify({
  message: "Server starting",
  operation: 'builder.createNodeServer',
  operation_startTime: createNodeServerStartTime,
  type: "READINESS",
  startTime,
  metadata: {
    teamId: "${TEAM_ID}",
    projectId: "${PROJECT_ID}",
    buildId: "${BUILD_ID}",
  },
}));

const transports = {}
if (process.env.CI !== 'true') {
  if (process.env.BUSINESS_API_RUNNER_LOGS_ENDPOINT) {
    transports.default = new HttpTransport({
      url: process.env.BUSINESS_API_RUNNER_LOGS_ENDPOINT,
      headers: {
        Authorization: 'Bearer ' + process.env.BUSINESS_JWT_TOKEN,
      },
    });
  }
}

const logger = new PinoLogger({
  name: 'MastraCloud',
  transports,
  level: 'debug',
});
const existingLogger = mastra?.getLogger();
const combinedLogger = existingLogger ? new MultiLogger([logger, existingLogger]) : logger;

mastra.setLogger({ logger: combinedLogger });

if (process.env.MASTRA_STORAGE_URL && process.env.MASTRA_STORAGE_AUTH_TOKEN) {
  const { MastraStorage } = await import('@mastra/core/storage');
  logger.info('Using Mastra Cloud Storage: ' + process.env.MASTRA_STORAGE_URL)
  const storage = new LibSQLStore({
    id: 'mastra-cloud-storage-libsql',
    url: process.env.MASTRA_STORAGE_URL,
    authToken: process.env.MASTRA_STORAGE_AUTH_TOKEN,
  })
  const vector = new LibSQLVector({
    id: 'mastra-cloud-storage-libsql-vector',
    url: process.env.MASTRA_STORAGE_URL,
    authToken: process.env.MASTRA_STORAGE_AUTH_TOKEN,
  })

  await storage.init()
  mastra?.setStorage(storage)
} else {
  const userStorage = mastra?.getStorage();
  if (userStorage && !userStorage.disableInit) {
    userStorage.init();
  }
}

if (mastra?.getStorage()) {
  mastra.__registerInternalWorkflow(scoreTracesWorkflow);
}

${getAuthEntrypoint()}

await createNodeServer(mastra, { studio: ${this.studio}, swaggerUI: false, tools: getToolExports(tools) });

${successEntrypoint()}

console.log(JSON.stringify({
  message: "Server started",
  operation: 'builder.createNodeServer',
  operation_startTime: createNodeServerStartTime,
  operation_durationMs: Date.now() - createNodeServerStartTime,
  type: "READINESS",
  startTime,
  metadata: {
    teamId: "${TEAM_ID}",
    projectId: "${PROJECT_ID}",
    buildId: "${BUILD_ID}",
  },
}));


console.log(JSON.stringify({
  message: "Runner Initialized",
  type: "READINESS",
  startTime,
  durationMs: Date.now() - startTime,
  metadata: {
    teamId: "${TEAM_ID}",
    projectId: "${PROJECT_ID}",
    buildId: "${BUILD_ID}",
  },
}));
`;
  }
};

export { CloudDeployer };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map