Make KERNEL_API_KEY optional for external credential injection (#687)
* Make KERNEL_API_KEY optional for external credential injection When running inside environments with external credential injection (e.g. Vercel Sandbox credentials brokering), the KERNEL_API_KEY env var can be omitted. The network layer injects the Authorization header on outbound requests to api.onkernel.com, so the API key never needs to exist inside the sandbox. If KERNEL_API_KEY is set, it's used as before. If not, requests are sent without an Authorization header, allowing external injection. Without either, the Kernel API returns 401. Made-with: Cursor * Make KERNEL_API_KEY optional in native Rust daemon too Applies the same change to the native Rust connect_kernel() function so both the Node.js and native code paths support external credential injection. Made-with: Cursor * Address review feedback: fix type errors, cargo fmt, always send cleanup DELETE - Fix kernelApiKey assignment: use ?? null for undefined -> null - Fix closeKernelSession signature: accept string | undefined - Always send DELETE on cleanup even without local API key (external injection covers it) - Run cargo fmt on Rust code Made-with: Cursor
This commit is contained in:
@@ -185,8 +185,7 @@ async fn connect_browser_use() -> Result<(String, Option<ProviderSession>), Stri
|
||||
}
|
||||
|
||||
async fn connect_kernel() -> Result<(String, Option<ProviderSession>), String> {
|
||||
let api_key =
|
||||
env::var("KERNEL_API_KEY").map_err(|_| "KERNEL_API_KEY environment variable is not set")?;
|
||||
let api_key = env::var("KERNEL_API_KEY").ok();
|
||||
let endpoint =
|
||||
env::var("KERNEL_ENDPOINT").unwrap_or_else(|_| "https://api.onkernel.com".to_string());
|
||||
|
||||
@@ -218,10 +217,11 @@ async fn connect_kernel() -> Result<(String, Option<ProviderSession>), String> {
|
||||
}
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.post(&url)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", format!("Bearer {}", api_key))
|
||||
let mut request = client.post(&url).header("Content-Type", "application/json");
|
||||
if let Some(ref key) = api_key {
|
||||
request = request.header("Authorization", format!("Bearer {}", key));
|
||||
}
|
||||
let response = request
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
|
||||
+29
-20
@@ -891,12 +891,14 @@ export class BrowserManager {
|
||||
/**
|
||||
* Close a Kernel session via API
|
||||
*/
|
||||
private async closeKernelSession(sessionId: string, apiKey: string): Promise<void> {
|
||||
private async closeKernelSession(sessionId: string, apiKey: string | undefined): Promise<void> {
|
||||
const headers: Record<string, string> = {};
|
||||
if (apiKey) {
|
||||
headers['Authorization'] = `Bearer ${apiKey}`;
|
||||
}
|
||||
const response = await fetch(`https://api.onkernel.com/browsers/${sessionId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -974,16 +976,19 @@ export class BrowserManager {
|
||||
*/
|
||||
private async findOrCreateKernelProfile(
|
||||
profileName: string,
|
||||
apiKey: string
|
||||
apiKey: string | undefined
|
||||
): Promise<{ name: string }> {
|
||||
const headers: Record<string, string> = {};
|
||||
if (apiKey) {
|
||||
headers['Authorization'] = `Bearer ${apiKey}`;
|
||||
}
|
||||
|
||||
// First, try to get the existing profile
|
||||
const getResponse = await fetch(
|
||||
`https://api.onkernel.com/profiles/${encodeURIComponent(profileName)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
headers,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1001,7 +1006,7 @@ export class BrowserManager {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
...headers,
|
||||
},
|
||||
body: JSON.stringify({ name: profileName }),
|
||||
});
|
||||
@@ -1015,13 +1020,13 @@ export class BrowserManager {
|
||||
|
||||
/**
|
||||
* Connect to Kernel remote browser via CDP.
|
||||
* Requires KERNEL_API_KEY environment variable.
|
||||
* Uses KERNEL_API_KEY environment variable for authentication when set.
|
||||
* When running inside environments with external credential injection
|
||||
* (e.g. Vercel Sandbox credentials brokering), the API key can be omitted
|
||||
* and auth headers will be injected at the network layer.
|
||||
*/
|
||||
private async connectToKernel(): Promise<void> {
|
||||
const kernelApiKey = process.env.KERNEL_API_KEY;
|
||||
if (!kernelApiKey) {
|
||||
throw new Error('KERNEL_API_KEY is required when using kernel as a provider');
|
||||
}
|
||||
|
||||
// Find or create profile if KERNEL_PROFILE_NAME is set
|
||||
const profileName = process.env.KERNEL_PROFILE_NAME;
|
||||
@@ -1037,12 +1042,16 @@ export class BrowserManager {
|
||||
};
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (kernelApiKey) {
|
||||
headers['Authorization'] = `Bearer ${kernelApiKey}`;
|
||||
}
|
||||
|
||||
const response = await fetch('https://api.onkernel.com/browsers', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${kernelApiKey}`,
|
||||
},
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
// Kernel browsers are headful by default with stealth mode available
|
||||
// The user can configure these via environment variables if needed
|
||||
@@ -1093,7 +1102,7 @@ export class BrowserManager {
|
||||
}
|
||||
|
||||
this.kernelSessionId = session.session_id;
|
||||
this.kernelApiKey = kernelApiKey;
|
||||
this.kernelApiKey = kernelApiKey ?? null;
|
||||
this.browser = browser;
|
||||
context.setDefaultTimeout(getDefaultTimeout());
|
||||
this.contexts.push(context);
|
||||
@@ -2513,8 +2522,8 @@ export class BrowserManager {
|
||||
}
|
||||
);
|
||||
this.browser = null;
|
||||
} else if (this.kernelSessionId && this.kernelApiKey) {
|
||||
await this.closeKernelSession(this.kernelSessionId, this.kernelApiKey).catch((error) => {
|
||||
} else if (this.kernelSessionId) {
|
||||
await this.closeKernelSession(this.kernelSessionId, this.kernelApiKey ?? undefined).catch((error) => {
|
||||
console.error('Failed to close Kernel session:', error);
|
||||
});
|
||||
this.browser = null;
|
||||
|
||||
Reference in New Issue
Block a user