-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[STG-2407] fix(cli): honor BROWSERBASE_API_KEY passed to an already-running daemon #2280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
shrey150
wants to merge
1
commit into
main
Choose a base branch
from
shrey/fix-daemon-inline-api-key
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "browse": patch | ||
| --- | ||
|
|
||
| Honor `BROWSERBASE_API_KEY` passed to an already-running driver daemon. Previously, if the first remote command started the daemon without a key, a later `BROWSERBASE_API_KEY=… browse open <url> --remote` (or an exported key in a new shell) kept failing with "Missing BROWSERBASE_API_KEY" because the detached daemon captured `process.env` once at spawn time and never saw the new key. The client now forwards the caller's key over the (localhost, owner-only) driver socket with every command, and the daemon threads it straight into the Stagehand constructor when it creates the session — so an inline or exported key works without a manual `browse stop` and restart. The forwarded key is never written back into the daemon's `process.env`; its only home is the live session. Already-initialized warm sessions are untouched; the forwarded key only takes effect at session init. The local-only (CDP-only) build forwards nothing and remains free of any API-key code path. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import { createHash } from "node:crypto"; | ||
|
|
||
| import { getRemote } from "../remote-binding.js"; | ||
|
|
||
| /** | ||
| * Credentials the client forwards to a running daemon so that an inline or | ||
| * exported API key set *after* the daemon started is still honored. | ||
| * | ||
| * The daemon is a detached background process whose `process.env` is captured | ||
| * once at spawn time. A key set on a *later* client invocation never reaches | ||
| * that process on its own, so the client ships the relevant credentials over | ||
| * the (localhost, owner-only) driver socket with every command. The daemon | ||
| * threads them straight into the Stagehand constructor at init — it never | ||
| * writes them back into its own `process.env`, so the key's only home is the | ||
| * live session, not the daemon's global environment. | ||
| * | ||
| * The *list* of forwardable keys is Browserbase-specific and therefore lives | ||
| * behind the remote capability (`remote.ts`), which the local-only build | ||
| * excludes. That keeps the literal key names out of the CDP-only artifact, the | ||
| * same security contract that confines `BROWSERBASE_API_KEY` reads to | ||
| * `remote.ts`. The signature side iterates the received object's own keys, so | ||
| * it needs no key list and stays key-name-free. | ||
| */ | ||
| export type ForwardedCredentials = Record<string, string>; | ||
|
|
||
| /** Collect the forwardable credentials that are set in the caller's env. */ | ||
| export async function collectClientCredentials( | ||
| env: NodeJS.ProcessEnv = process.env, | ||
| ): Promise<ForwardedCredentials | undefined> { | ||
| const keys = (await getRemote()).forwardedCredentialKeys(); | ||
| const credentials: ForwardedCredentials = {}; | ||
| for (const key of keys) { | ||
| const value = env[key]; | ||
| if (typeof value === "string" && value.length > 0) { | ||
| credentials[key] = value; | ||
| } | ||
| } | ||
| return Object.keys(credentials).length > 0 ? credentials : undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Stable, secret-free fingerprint of a forwarded credential set, used only to | ||
| * detect whether the caller's credentials changed between requests (so a cold | ||
| * session can bust its cached init-failure backoff and retry with the new key). | ||
| * Hashing keeps the raw key out of any retained field. Iterates the received | ||
| * object's own keys — the client already filtered to the forwardable set — so | ||
| * this carries no literal key names. Returns "" for an empty/absent set. | ||
| */ | ||
| export function credentialSignature( | ||
| credentials: ForwardedCredentials | undefined, | ||
| ): string { | ||
| if (!credentials) return ""; | ||
| const entries = Object.entries(credentials) | ||
| .filter(([, value]) => typeof value === "string" && value.length > 0) | ||
| .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)); | ||
| if (entries.length === 0) return ""; | ||
| const hash = createHash("sha256"); | ||
| for (const [key, value] of entries) { | ||
| hash.update(key); | ||
| hash.update("\0"); | ||
| hash.update(value); | ||
| hash.update("\0"); | ||
| } | ||
| return hash.digest("hex"); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.