Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cli-attach-function-secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add `browse functions secrets attach` to attach an existing project secret to a function by ID.
5 changes: 5 additions & 0 deletions .changeset/cli-create-secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add `browse cloud secrets create` with public-key lookup, local encryption, and secret input from stdin, a named environment variable, or a hidden prompt.
5 changes: 5 additions & 0 deletions .changeset/cli-detach-function-secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add `browse functions secrets detach` to remove a function-secret attachment without deleting the project secret.
5 changes: 5 additions & 0 deletions .changeset/cli-get-delete-secrets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add commands to retrieve project secret metadata and delete a project secret by ID.
5 changes: 5 additions & 0 deletions .changeset/cli-list-function-secrets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add `browse functions secrets list` to list attached secret metadata with cursor pagination and creation-time filters.
5 changes: 5 additions & 0 deletions .changeset/cli-list-project-secrets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add `browse cloud secrets list` to list project secret metadata with pagination and date filters.
5 changes: 5 additions & 0 deletions .changeset/cli-update-secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"browse": minor
---

Add `browse cloud secrets update` to replace a secret value by ID with local encryption and stdin, environment variable, or hidden prompt input.
9 changes: 9 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"cloud": {
"description": "Manage Browserbase cloud resources and APIs."
},
"cloud:secrets": {
"description": "Create, list, retrieve, update, and delete project secrets."
},
"cloud:projects": {
"description": "Manage Browserbase projects."
},
Expand All @@ -54,6 +57,9 @@
"cloud:sessions:uploads": {
"description": "Upload files to Browserbase sessions."
},
"functions:secrets": {
"description": "List, attach, and detach project secrets for functions."
},
"functions": {
"description": "Develop, publish, and invoke Browserbase Functions."
},
Expand Down Expand Up @@ -104,6 +110,9 @@
"dependencies": {
"@browserbasehq/sdk": "^2.14.0",
"@browserbasehq/stagehand": "workspace:*",
"@hpke/core": "^1.9.0",
"@hpke/dhkem-x25519": "^1.8.0",
"@inquirer/password": "^4.0.23",
"@oclif/core": "^4.11.0",
"@vercel/detect-agent": "^1.2.3",
"archiver": "^7.0.1",
Expand Down
34 changes: 34 additions & 0 deletions packages/cli/src/commands/cloud/secrets/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { createSecret } from "../../../lib/secrets/api.js";
import { readSecretValue } from "../../../lib/secrets/input.js";
import { secretInputFlags } from "../../../lib/secrets/flags.js";
import { outputJson } from "../../../lib/output.js";

export default class SecretsCreate extends BrowseCommand {
static override description =
"Create a project secret. Encrypts the value locally with the project public key.";
static override examples = [
"browse cloud secrets create SERVICE_TOKEN",
"browse cloud secrets create SERVICE_TOKEN --env MY_SERVICE_TOKEN",
"browse cloud secrets create SERVICE_TOKEN --stdin < ./secret.txt",
];
static override args = {
key: Args.string({
description: "Name exposed in the function context.secrets object.",
required: true,
}),
};
static override flags = { ...apiCommonFlags, ...secretInputFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(SecretsCreate);
const options = toApiOptions(flags);
const value = await readSecretValue({ stdin: flags.stdin, env: flags.env });
try {
outputJson(await createSecret(options, args.key, value));
} finally {
value.fill(0);
}
}
}
25 changes: 25 additions & 0 deletions packages/cli/src/commands/cloud/secrets/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { deleteSecret } from "../../../lib/secrets/api.js";

export default class SecretsDelete extends BrowseCommand {
static override description = "Delete a project secret.";
static override examples = [
"browse cloud secrets delete <secretId>",
"browse cloud secrets delete d2c4f48f-38e9-4b82-a36a-2b373fd14a65",
];
static override args = {
secretId: Args.string({
description:
"Project secret ID (e.g. d2c4f48f-38e9-4b82-a36a-2b373fd14a65).",
required: true,
}),
};
static override flags = { ...apiCommonFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(SecretsDelete);
const options = toApiOptions(flags);
await deleteSecret(options, args.secretId);
}
}
27 changes: 27 additions & 0 deletions packages/cli/src/commands/cloud/secrets/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { getSecret } from "../../../lib/secrets/api.js";
import { outputJson } from "../../../lib/output.js";

export default class SecretsGet extends BrowseCommand {
static override description =
"Get project secret metadata. Does not return the secret value.";
static override examples = [
"browse cloud secrets get <secretId>",
"browse cloud secrets get d2c4f48f-38e9-4b82-a36a-2b373fd14a65",
];
static override args = {
secretId: Args.string({
description:
"Project secret ID (e.g. d2c4f48f-38e9-4b82-a36a-2b373fd14a65).",
required: true,
}),
};
static override flags = { ...apiCommonFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(SecretsGet);
const options = toApiOptions(flags);
outputJson(await getSecret(options, args.secretId));
}
}
25 changes: 25 additions & 0 deletions packages/cli/src/commands/cloud/secrets/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { listSecrets } from "../../../lib/secrets/api.js";
import {
listSecretsFlags,
toListSecretsOptions,
} from "../../../lib/secrets/flags.js";
import { outputJson } from "../../../lib/output.js";

export default class SecretsList extends BrowseCommand {
static override description =
"List project secret metadata with cursor pagination.";
static override examples = [
"browse cloud secrets list",
"browse cloud secrets list --start-at 2026-01-01T00:00:00Z",
"browse cloud secrets list --start-at 2026-01-01T00:00:00Z --end-at 2026-02-01T00:00:00Z",
"browse cloud secrets list --limit 10",
];
static override flags = { ...apiCommonFlags, ...listSecretsFlags };
async run(): Promise<void> {
const { flags } = await this.parse(SecretsList);
const options = toApiOptions(flags);
outputJson(await listSecrets(options, toListSecretsOptions(flags)));
}
}
36 changes: 36 additions & 0 deletions packages/cli/src/commands/cloud/secrets/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { updateSecret } from "../../../lib/secrets/api.js";
import { readSecretValue } from "../../../lib/secrets/input.js";
import { secretInputFlags } from "../../../lib/secrets/flags.js";
import { outputJson } from "../../../lib/output.js";

export default class SecretsUpdate extends BrowseCommand {
static override description =
"Replace a secret value, encrypting it locally with the current project public key.";
static override examples = [
"browse cloud secrets update <secretId>",
"browse cloud secrets update d2c4f48f-38e9-4b82-a36a-2b373fd14a65",
"browse cloud secrets update <secretId> --env MY_SERVICE_TOKEN",
"browse cloud secrets update <secretId> --stdin < ./secret.txt",
];
static override args = {
secretId: Args.string({
description:
"Project secret ID (e.g. d2c4f48f-38e9-4b82-a36a-2b373fd14a65).",
required: true,
}),
};
static override flags = { ...apiCommonFlags, ...secretInputFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(SecretsUpdate);
const options = toApiOptions(flags);
const value = await readSecretValue({ stdin: flags.stdin, env: flags.env });
try {
outputJson(await updateSecret(options, args.secretId, value));
} finally {
value.fill(0);
}
}
}
30 changes: 30 additions & 0 deletions packages/cli/src/commands/functions/secrets/attach.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { attachFunctionSecret } from "../../../lib/secrets/api.js";

export default class FunctionSecretsAttach extends BrowseCommand {
static override description =
"Attach an existing project secret to a function.";
static override examples = [
"browse functions secrets attach <functionId> <secretId>",
"browse functions secrets attach 7b6e1c42-8d93-4a15-b2f0-9c6d3e8a5041 d2c4f48f-38e9-4b82-a36a-2b373fd14a65",
];
static override args = {
functionId: Args.string({
description: "Function ID (e.g. 7b6e1c42-8d93-4a15-b2f0-9c6d3e8a5041).",
required: true,
}),
secretId: Args.string({
description:
"Project secret ID (e.g. d2c4f48f-38e9-4b82-a36a-2b373fd14a65).",
required: true,
}),
};
static override flags = { ...apiCommonFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(FunctionSecretsAttach);
const options = toApiOptions(flags);
await attachFunctionSecret(options, args.functionId, args.secretId);
}
}
30 changes: 30 additions & 0 deletions packages/cli/src/commands/functions/secrets/detach.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { detachFunctionSecret } from "../../../lib/secrets/api.js";

export default class FunctionSecretsDetach extends BrowseCommand {
static override description =
"Detach a secret from a function without deleting the secret.";
static override examples = [
"browse functions secrets detach <functionId> <secretId>",
"browse functions secrets detach 7b6e1c42-8d93-4a15-b2f0-9c6d3e8a5041 d2c4f48f-38e9-4b82-a36a-2b373fd14a65",
];
static override args = {
functionId: Args.string({
description: "Function ID (e.g. 7b6e1c42-8d93-4a15-b2f0-9c6d3e8a5041).",
required: true,
}),
secretId: Args.string({
description:
"Project secret ID (e.g. d2c4f48f-38e9-4b82-a36a-2b373fd14a65).",
required: true,
}),
};
static override flags = { ...apiCommonFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(FunctionSecretsDetach);
const options = toApiOptions(flags);
await detachFunctionSecret(options, args.functionId, args.secretId);
}
}
39 changes: 39 additions & 0 deletions packages/cli/src/commands/functions/secrets/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Args } from "@oclif/core";
import { BrowseCommand } from "../../../base.js";
import { apiCommonFlags, toApiOptions } from "../../../lib/cloud/flags.js";
import { listFunctionSecrets } from "../../../lib/secrets/api.js";
import {
listSecretsFlags,
toListSecretsOptions,
} from "../../../lib/secrets/flags.js";
import { outputJson } from "../../../lib/output.js";

export default class FunctionSecretsList extends BrowseCommand {
static override description =
"List metadata for secrets attached to a function with cursor pagination.";
static override examples = [
"browse functions secrets list <functionId>",
"browse functions secrets list 7b6e1c42-8d93-4a15-b2f0-9c6d3e8a5041",
"browse functions secrets list <functionId> --start-at 2026-01-01T00:00:00Z",
"browse functions secrets list <functionId> --start-at 2026-01-01T00:00:00Z --end-at 2026-02-01T00:00:00Z",
"browse functions secrets list <functionId> --limit 10",
];
static override args = {
functionId: Args.string({
description: "Function ID (e.g. 7b6e1c42-8d93-4a15-b2f0-9c6d3e8a5041).",
required: true,
}),
};
static override flags = { ...apiCommonFlags, ...listSecretsFlags };
async run(): Promise<void> {
const { args, flags } = await this.parse(FunctionSecretsList);
const options = toApiOptions(flags);
outputJson(
await listFunctionSecrets(
options,
args.functionId,
toListSecretsOptions(flags),
),
);
}
}
5 changes: 5 additions & 0 deletions packages/cli/src/lib/cloud/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type BrowserbaseApiCommand =
| "contexts"
| "extensions"
| "functions"
| "secrets"
| "sessions";

export function resolveApiKey(args: { apiKey?: string }): string {
Expand Down Expand Up @@ -442,6 +443,10 @@ function resolveCommandFromPathname(
return "extensions";
}

if (pathname.startsWith("/v1/secrets")) {
return "secrets";
}

if (pathname.startsWith("/v1/functions")) {
return "functions";
}
Expand Down
Loading
Loading