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,
};
}