mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
Compare commits
3 Commits
fix/cli-re
...
fix/mar26-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc90766714 | ||
|
|
de50c5b378 | ||
|
|
ffe48948ff |
19
README.md
19
README.md
@@ -43,6 +43,24 @@
|
||||
|
||||
4. Start automating!
|
||||
|
||||
## Install `browseros-cli`
|
||||
|
||||
Use `browseros-cli` when you want to control BrowserOS from the terminal or scripts via the BrowserOS MCP server.
|
||||
|
||||
### macOS / Linux
|
||||
|
||||
```bash
|
||||
curl -fsSL https://cdn.browseros.com/cli/install.sh | bash
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
irm https://cdn.browseros.com/cli/install.ps1 | iex
|
||||
```
|
||||
|
||||
After install, run `browseros-cli init` to point the CLI at your BrowserOS MCP server.
|
||||
|
||||
## What makes BrowserOS special
|
||||
- 🏠 Feels like home — same Chrome interface, all your extensions just work
|
||||
- 🤖 AI agents that run on YOUR browser, not in the cloud
|
||||
@@ -164,4 +182,3 @@ Thank you to all our supporters!
|
||||
Built with ❤️ from San Francisco
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Production upload env for CLI installer scripts
|
||||
|
||||
R2_ACCOUNT_ID=
|
||||
R2_ACCESS_KEY_ID=
|
||||
R2_SECRET_ACCESS_KEY=
|
||||
R2_BUCKET=browseros
|
||||
R2_UPLOAD_PREFIX=cli
|
||||
@@ -2,12 +2,12 @@
|
||||
# Install browseros-cli for Windows — downloads the latest release binary.
|
||||
#
|
||||
# Usage (PowerShell — save and run):
|
||||
# Invoke-WebRequest -Uri "https://raw.githubusercontent.com/browseros-ai/BrowserOS/main/packages/browseros-agent/apps/cli/scripts/install.ps1" -OutFile install.ps1
|
||||
# Invoke-WebRequest -Uri "https://cdn.browseros.com/cli/install.ps1" -OutFile install.ps1
|
||||
# .\install.ps1
|
||||
# .\install.ps1 -Version "0.1.0" -Dir "C:\tools\browseros"
|
||||
#
|
||||
# Usage (one-liner, uses env vars for options):
|
||||
# & { $env:BROWSEROS_VERSION="0.1.0"; irm https://raw.githubusercontent.com/browseros-ai/BrowserOS/main/packages/browseros-agent/apps/cli/scripts/install.ps1 | iex }
|
||||
# & { $env:BROWSEROS_VERSION="0.1.0"; irm https://cdn.browseros.com/cli/install.ps1 | iex }
|
||||
#
|
||||
|
||||
param(
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
# Install browseros-cli — downloads the latest release binary for your platform.
|
||||
#
|
||||
# Usage:
|
||||
# curl -fsSL https://raw.githubusercontent.com/browseros-ai/BrowserOS/main/packages/browseros-agent/apps/cli/scripts/install.sh | bash
|
||||
# curl -fsSL https://cdn.browseros.com/cli/install.sh | bash
|
||||
#
|
||||
# # Or with options:
|
||||
# curl -fsSL ... | bash -s -- --version 0.1.0 --dir /usr/local/bin
|
||||
# curl -fsSL https://cdn.browseros.com/cli/install.sh | bash -s -- --version 0.1.0 --dir /usr/local/bin
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
},
|
||||
"packages/agent-sdk": {
|
||||
"name": "@browseros-ai/agent-sdk",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.7",
|
||||
"dependencies": {
|
||||
"eventsource-parser": "^3.0.6",
|
||||
"zod-to-json-schema": "^3.24.1",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"build:server": "FORCE_COLOR=1 bun scripts/build/server.ts --target=all",
|
||||
"build:server:ci": "FORCE_COLOR=1 bun scripts/build/server.ts --target=all --compile-only",
|
||||
"build:server:test": "FORCE_COLOR=1 bun scripts/build/server.ts --target=darwin-arm64 --no-upload",
|
||||
"upload:cli-installers": "bun scripts/build/cli.ts",
|
||||
"start:server:test": "bun run build:server:test && set -a && . apps/server/.env.development && set +a && dist/prod/server/.tmp/binaries/browseros-server-darwin-arm64",
|
||||
"build:agent:dev": "FORCE_COLOR=1 bun run --filter @browseros/agent --elide-lines=0 build:dev",
|
||||
"build:agent": "bun run codegen:agent && bun run --filter @browseros/agent build",
|
||||
|
||||
9
packages/browseros-agent/scripts/build/cli.ts
Normal file
9
packages/browseros-agent/scripts/build/cli.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { runCliInstallerUpload } from './cli/upload'
|
||||
|
||||
runCliInstallerUpload().catch((error) => {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
console.error(`\n✗ ${message}\n`)
|
||||
process.exit(1)
|
||||
})
|
||||
52
packages/browseros-agent/scripts/build/cli/config.ts
Normal file
52
packages/browseros-agent/scripts/build/cli/config.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
|
||||
import { parse } from 'dotenv'
|
||||
|
||||
import type { R2Config } from '../server/types'
|
||||
|
||||
const PROD_ENV_PATH = join('apps', 'cli', '.env.production')
|
||||
const PROD_ENV_TEMPLATE_PATH = join('apps', 'cli', '.env.production.example')
|
||||
|
||||
function pickEnv(name: string, fileEnv: Record<string, string>): string {
|
||||
const value = process.env[name] ?? fileEnv[name]
|
||||
if (!value || value.trim().length === 0) {
|
||||
throw new Error(`Missing required environment variable: ${name}`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function loadProdEnv(rootDir: string): Record<string, string> {
|
||||
const prodEnvPath = join(rootDir, PROD_ENV_PATH)
|
||||
if (!existsSync(prodEnvPath)) {
|
||||
const templatePath = join(rootDir, PROD_ENV_TEMPLATE_PATH)
|
||||
if (existsSync(templatePath)) {
|
||||
throw new Error(
|
||||
`Missing ${PROD_ENV_PATH}. Create it from ${PROD_ENV_TEMPLATE_PATH} before running upload:cli-installers.`,
|
||||
)
|
||||
}
|
||||
throw new Error(
|
||||
`Missing ${PROD_ENV_PATH}. The template file ${PROD_ENV_TEMPLATE_PATH} was not found.`,
|
||||
)
|
||||
}
|
||||
return parse(readFileSync(prodEnvPath, 'utf-8'))
|
||||
}
|
||||
|
||||
export interface CliUploadConfig {
|
||||
r2: R2Config
|
||||
}
|
||||
|
||||
export function loadCliUploadConfig(rootDir: string): CliUploadConfig {
|
||||
const fileEnv = loadProdEnv(rootDir)
|
||||
return {
|
||||
r2: {
|
||||
accountId: pickEnv('R2_ACCOUNT_ID', fileEnv),
|
||||
accessKeyId: pickEnv('R2_ACCESS_KEY_ID', fileEnv),
|
||||
secretAccessKey: pickEnv('R2_SECRET_ACCESS_KEY', fileEnv),
|
||||
bucket: pickEnv('R2_BUCKET', fileEnv),
|
||||
downloadPrefix: '',
|
||||
uploadPrefix:
|
||||
process.env.R2_UPLOAD_PREFIX ?? fileEnv.R2_UPLOAD_PREFIX ?? 'cli',
|
||||
},
|
||||
}
|
||||
}
|
||||
56
packages/browseros-agent/scripts/build/cli/upload.ts
Normal file
56
packages/browseros-agent/scripts/build/cli/upload.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { log } from '../log'
|
||||
import { createR2Client, joinObjectKey, uploadFileToObject } from '../server/r2'
|
||||
import { loadCliUploadConfig } from './config'
|
||||
|
||||
const CDN_BASE_URL = 'https://cdn.browseros.com'
|
||||
|
||||
const INSTALLERS = [
|
||||
{
|
||||
filePath: join('apps', 'cli', 'scripts', 'install.sh'),
|
||||
objectName: 'install.sh',
|
||||
contentType: 'text/x-shellscript; charset=utf-8',
|
||||
},
|
||||
{
|
||||
filePath: join('apps', 'cli', 'scripts', 'install.ps1'),
|
||||
objectName: 'install.ps1',
|
||||
contentType: 'text/plain; charset=utf-8',
|
||||
},
|
||||
] as const
|
||||
|
||||
export async function runCliInstallerUpload(): Promise<void> {
|
||||
const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), '../../..')
|
||||
process.chdir(rootDir)
|
||||
await uploadCliInstallers(rootDir)
|
||||
}
|
||||
|
||||
export async function uploadCliInstallers(rootDir: string): Promise<void> {
|
||||
const { r2 } = loadCliUploadConfig(rootDir)
|
||||
const client = createR2Client(r2)
|
||||
|
||||
log.header('Uploading BrowserOS CLI installer scripts')
|
||||
|
||||
try {
|
||||
for (const installer of INSTALLERS) {
|
||||
const absolutePath = join(rootDir, installer.filePath)
|
||||
if (!existsSync(absolutePath)) {
|
||||
throw new Error(`Installer script not found: ${installer.filePath}`)
|
||||
}
|
||||
|
||||
const objectKey = joinObjectKey(r2.uploadPrefix, installer.objectName)
|
||||
log.step(`Uploading ${installer.filePath}`)
|
||||
await uploadFileToObject(client, r2, objectKey, absolutePath, {
|
||||
contentType: installer.contentType,
|
||||
})
|
||||
log.success(`Uploaded ${objectKey}`)
|
||||
log.info(`${CDN_BASE_URL}/${objectKey}`)
|
||||
}
|
||||
|
||||
log.done('CLI installer upload completed')
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
|
||||
import type { R2Config } from './types'
|
||||
|
||||
export interface UploadFileOptions {
|
||||
contentType?: string
|
||||
}
|
||||
|
||||
function createClientConfig(r2: R2Config): S3ClientConfig {
|
||||
return {
|
||||
region: 'auto',
|
||||
@@ -81,6 +85,7 @@ export async function uploadFileToObject(
|
||||
r2: R2Config,
|
||||
key: string,
|
||||
filePath: string,
|
||||
options: UploadFileOptions = {},
|
||||
): Promise<void> {
|
||||
const data = await readFile(filePath)
|
||||
await client.send(
|
||||
@@ -88,7 +93,7 @@ export async function uploadFileToObject(
|
||||
Bucket: r2.bucket,
|
||||
Key: key,
|
||||
Body: data,
|
||||
ContentType: 'application/zip',
|
||||
ContentType: options.contentType ?? 'application/zip',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user