private async submitJobToApi()

in app/lib/server/processors/ApiJobExecutor.ts [137:275]


  private async submitJobToApi(jobId: string, jobData: any, credentials: any) {
    // Use credentials from request (required - no fallback)
    const hfToken = credentials?.huggingfaceToken;
    const openaiKey = credentials?.openaiApiKey;
    const githubToken = credentials?.githubToken;

    if (!hfToken) {
      throw new Error(
        "Hugging Face token is required but not provided in credentials"
      );
    }

    const username = getEffectiveUsername(credentials);

    // Get repository URL from job data or fall back to server config
    const repositoryUrl = jobData.repository?.url || REPO.URL;
    const repositoryBranch = jobData.repository?.branch || REPO.BRANCH;

    // Handle GitHub repository access for private repos
    let authenticatedRepoUrl = repositoryUrl;
    let githubEphemeralToken = null;

    if (githubToken && repositoryUrl.includes("github.com")) {
      console.log("🔑 GitHub token available, checking repository access...");

      // Validate repository access
      const repoAccess = await GitHubTokenService.validateRepositoryAccess(
        githubToken,
        repositoryUrl
      );

      if (repoAccess.canAccess) {
        console.log(
          `📂 Repository access confirmed (private: ${repoAccess.isPrivate})`
        );

        // Create ephemeral token for container use (expires in 60 minutes)
        const ephemeralResult = await GitHubTokenService.createEphemeralToken(
          githubToken,
          repositoryUrl,
          60 // 60 minutes
        );

        if (ephemeralResult) {
          githubEphemeralToken = ephemeralResult.token;
          // Create authenticated clone URL for the container
          authenticatedRepoUrl = GitHubTokenService.createAuthenticatedCloneUrl(
            ephemeralResult.token,
            repositoryUrl
          );
          console.log("🎫 Ephemeral GitHub token created for container access");
        } else {
          console.warn(
            "⚠️ Could not create ephemeral token, using original URL"
          );
        }
      } else {
        console.warn("⚠️ Cannot access repository with provided GitHub token");
      }
    }

    // Merge base environment with user's custom environment variables from job data
    const environment = {
      JOB_ID: jobId,
      REPO_URL: authenticatedRepoUrl, // Use authenticated URL if available
      REPO_BRANCH: repositoryBranch,
      // PROMPT: `Clone the repository, then change to the repository directory (${repositoryUrl.split("/").pop()?.replace(".git", "") || "repo"}) and execute the following task: ${jobData.description}. Make sure to stay within the repository directory for all operations and use file editing tools to make any necessary changes.`,
      PROMPT: jobData.description,
      ...dockerConfig.environment, // Global defaults
      ...(jobData.environment || {}), // Job-specific environment variables override global ones
    };

    // Merge required secrets with user's custom secrets from job data
    const secrets = {
      OPENAI_API_KEY: openaiKey,
      ...(githubEphemeralToken && { GITHUB_TOKEN: githubEphemeralToken }), // Add GitHub token if available
      ...dockerConfig.secrets, // Global secrets
      ...(jobData.secrets || {}), // Job-specific secrets override global ones
    };

    // Create job payload for Hugging Face API
    const payload = {
      command: ["/opt/agents/codex"],
      arguments: [],
      environment,
      flavor: "cpu-basic",
      dockerImage: dockerConfig.image,
      secrets,
      timeoutSeconds: HUGGINGFACE_API.TIMEOUT_SECONDS,
    };

    console.log("🔍 API Payload (environment and secrets debug):", {
      ...payload,
      environment: payload.environment, // Show actual environment variables
      secrets: Object.keys(payload.secrets).reduce(
        (acc, key) => {
          acc[key] = payload.secrets[key] ? "***" : "(not set)";
          return acc;
        },
        {} as Record<string, string>
      ),
    });

    // Construct URL with username from credentials
    // Format: https://huggingface.co/api/jobs/username
    const apiUrl = `${HUGGINGFACE_API.BASE_URL.replace(
      "/api/jobs/",
      `/api/jobs/${username}`
    )}`;
    console.log(`🔗 Using API URL with username: ${apiUrl}`);

    const response = await fetch(apiUrl, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${hfToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(
        `API request failed: ${response.status} ${response.statusText} - ${errorText}`
      );
    }

    const result = await response.json();
    const submittedApiJobId = result.id || result.jobId || result._id;
    console.log(
      `✅ Job submitted to API with ID: ${submittedApiJobId || "unknown"}`
    );

    return {
      apiJobId: submittedApiJobId,
      environment,
      secrets,
    };
  }