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
34 changes: 10 additions & 24 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ jobs:
run: bun install --frozen-lockfile
- name: Test
run: bun run test
- name: Build package
run: bun run --filter beeper-cli build
- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -38,25 +36,13 @@ jobs:
gh release create "${tag}" --title "${tag}" --generate-notes --verify-tag
fi

# Binary/Homebrew release assets are intentionally disabled until binary
# releases are ready to ship. Re-enable these steps with the package scripts
# already kept in packages/cli/package.json:
#
# - name: Build Homebrew archive
# run: bun run --filter beeper-cli pack:homebrew
#
# - name: Publish GitHub release assets
# env:
# GH_TOKEN: ${{ github.token }}
# run: |
# set -euo pipefail
# tag="${GITHUB_REF_NAME}"
# if ! gh release view "${tag}" >/dev/null 2>&1; then
# gh release create "${tag}" --title "${tag}" --generate-notes --verify-tag
# fi
# gh release upload "${tag}" packages/cli/dist/bin/beeper-* packages/cli/dist/bin/binaries.json packages/cli/dist/release/*.tar.gz packages/cli/dist/release/homebrew.json --clobber
#
# - name: Publish Homebrew formula
# env:
# HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
# run: bun scripts/publish-homebrew-formula.ts
- name: Build binary release assets
run: bun run --filter beeper-cli binary

- name: Publish GitHub release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME}"
gh release upload "${tag}" packages/cli/dist/bin/beeper-* packages/cli/dist/bin/binaries.json --clobber
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "desktop-api-cli-monorepo",
"name": "@beeper/cli",
"private": true,
"type": "module",
"packageManager": "bun@1.3.10",
Expand All @@ -16,6 +16,7 @@
"changeset": "changeset",
"lint": "eslint eslint.config.mjs packages/cli-plugin-cloudflare/src packages/cli-plugin-cloudflare/test",
"pack:packages": "mkdir -p .packs && (cd packages/cli && bun pm pack --destination ../../.packs) && (cd packages/cli-plugin-cloudflare && bun pm pack --destination ../../.packs)",
"publish:packages": "bun scripts/publish-packages.ts",
"release": "bun run check && bun changeset publish",
"test": "bun run --workspaces --sequential test",
"typecheck": "bun run --filter beeper-cli build && bun run --workspaces --sequential typecheck",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-plugin-cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@beeper/cli-plugin-cloudflare",
"version": "0.0.0",
"version": "0.6.0",
"description": "Cloudflare Tunnel commands for Beeper CLI",
"license": "MIT",
"type": "module",
Expand Down Expand Up @@ -34,7 +34,7 @@
},
"dependencies": {
"@oclif/core": "^4.11.2",
"beeper-cli": "workspace:*"
"beeper-cli": "^0.6.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
Expand Down
6 changes: 5 additions & 1 deletion packages/cli-plugin-cloudflare/src/lib/cloudflared.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { createWriteStream } from 'node:fs'
import { access, chmod, mkdir, rename, rm } from 'node:fs/promises'
import { arch, platform } from 'node:os'
import { basename, dirname, join } from 'node:path'
import { Readable } from 'node:stream'
import { pipeline } from 'node:stream/promises'
import type { ReadableStream } from 'node:stream/web'
import { fileURLToPath } from 'node:url'
import { execFileSync, spawn, type ChildProcess } from 'node:child_process'

Expand Down Expand Up @@ -193,7 +197,7 @@ function downloadURL(system = platform(), cpu = arch()): string {
async function downloadFile(url: string, to: string): Promise<void> {
const response = await fetch(url, { redirect: 'follow' })
if (!response.ok || !response.body) throw new Error(`Could not download ${url}: ${response.status} ${response.statusText}`)
await Bun.write(to, response)
await pipeline(Readable.fromWeb(response.body as unknown as ReadableStream), createWriteStream(to))
}

export function findTunnelURL(data: string, domain = cloudflaredDomain()): string | undefined {
Expand Down
78 changes: 63 additions & 15 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ First-party optional plugins:
| `targets tunnel` | Expose a local Desktop API over a public Cloudflare tunnel |
| `auth status` | Show stored auth for the selected target |
| `auth logout` | Clear stored authentication |
| `auth email start` | Start email sign-in for a target |
| `auth email response` | Finish email sign-in with a verification code |
| `verify` | Finish setup verification or verify another device |
| `verify status` | Show encryption and device-verification readiness |
| `verify approve` | Approve a pending device verification request |
Expand Down Expand Up @@ -421,14 +423,16 @@ Flags:

| Flag | Type | Description |
| --- | --- | --- |
| `--channel=<stable|nightly>` | option | Install release channel Default: stable |
| `--channel=<stable\|nightly>` | option | Install release channel Default: stable |
| `--desktop` | boolean | Set up a local Beeper Desktop target |
| `--email=<value>` | option | Sign in with an email address |
| `--install` | boolean | Allow installing missing managed runtime |
| `--local` | boolean | Use the local Beeper Desktop session on this device |
| `--oauth` | boolean | Authorize the target with browser OAuth/PKCE |
| `--remote=<value>` | option | Connect to a remote Beeper Desktop or Server URL |
| `--server` | boolean | Set up a local Beeper Server target |
| `--server-env=<production|staging>` | option | Server environment. Staging forces nightly. Default: production |
| `--server-env=<production\|staging>` | option | Server environment. Staging forces nightly. Default: production |
| `--username=<value>` | option | Username to use if setup creates a new account |

Examples:

Expand All @@ -453,7 +457,7 @@ Flags:

| Flag | Type | Description |
| --- | --- | --- |
| `--channel=<stable|nightly>` | option | Desktop release channel Default: stable |
| `--channel=<stable\|nightly>` | option | Desktop release channel Default: stable |

Examples:

Expand All @@ -475,8 +479,8 @@ Flags:

| Flag | Type | Description |
| --- | --- | --- |
| `--channel=<stable|nightly>` | option | Server release channel Default: stable |
| `--server-env=<production|staging>` | option | Server environment. Staging forces nightly. Default: production |
| `--channel=<stable\|nightly>` | option | Server release channel Default: stable |
| `--server-env=<production\|staging>` | option | Server environment. Staging forces nightly. Default: production |

Examples:

Expand Down Expand Up @@ -517,7 +521,7 @@ Flags:
| Flag | Type | Description |
| --- | --- | --- |
| `--available` | boolean | Only bridges available to add (--no-available to exclude) |
| `--provider=<local|cloud|self-hosted>` | option | Limit to bridge provider |
| `--provider=<local\|cloud\|self-hosted>` | option | Limit to bridge provider |

Examples:

Expand Down Expand Up @@ -569,7 +573,7 @@ Flags:
| --- | --- | --- |
| `--default` | boolean | Set this target as the default after creation |
| `--port=<value>` | option | TCP port the managed Desktop will expose its API on |
| `--server-env=<production|staging>` | option | Server environment. Staging forces nightly. Default: production |
| `--server-env=<production\|staging>` | option | Server environment. Staging forces nightly. Default: production |

Examples:

Expand Down Expand Up @@ -598,7 +602,7 @@ Flags:
| --- | --- | --- |
| `--default` | boolean | Set this target as the default after creation |
| `--port=<value>` | option | TCP port the managed Server will expose its API on |
| `--server-env=<production|staging>` | option | Server environment. Staging forces nightly. Default: production |
| `--server-env=<production\|staging>` | option | Server environment. Staging forces nightly. Default: production |

Examples:

Expand Down Expand Up @@ -902,6 +906,50 @@ beeper auth logout

Global flags: `--base-url`, `--target`, `--debug`, `--events`, `--full`, `--json`, `--quiet`, `--read-only`, `--timeout`, `--yes`.

### `beeper auth email start`
Start email sign-in for a target

```sh
beeper auth email start
```

Flags:

| Flag | Type | Description |
| --- | --- | --- |
| `--email=<value>` | option | Email address to sign in with Required. |

Examples:

```sh
beeper auth email start --email you@example.com --target work --json
```

Global flags: `--base-url`, `--target`, `--debug`, `--events`, `--full`, `--json`, `--quiet`, `--read-only`, `--timeout`, `--yes`.

### `beeper auth email response`
Finish email sign-in with a verification code

```sh
beeper auth email response
```

Flags:

| Flag | Type | Description |
| --- | --- | --- |
| `--code=<value>` | option | Email verification code Required. |
| `--setup-request-id=<value>` | option | Setup request ID from auth email start Required. |
| `--username=<value>` | option | Username to use if setup creates a new account |

Examples:

```sh
beeper auth email response --setup-request-id <id> --code <code> --target work --json
```

Global flags: `--base-url`, `--target`, `--debug`, `--events`, `--full`, `--json`, `--quiet`, `--read-only`, `--timeout`, `--yes`.

### `beeper verify`
Finish setup verification or verify another device

Expand Down Expand Up @@ -1202,7 +1250,7 @@ Flags:
| `--login-id=<value>` | option | Existing login ID to re-login as |
| `--non-interactive` | boolean | Do not prompt; require --flow, --field, and --cookie values when needed. |
| `--webview` | boolean | Use Bun.WebView to collect cookie login fields when a cookie step is returned. |
| `--webview-backend=<auto|chrome|webkit>` | option | Bun.WebView backend for cookie login steps. Default: chrome |
| `--webview-backend=<auto\|chrome\|webkit>` | option | Bun.WebView backend for cookie login steps. Default: chrome |
| `--webview-timeout=<value>` | option | Seconds to wait for Bun.WebView cookie collection. Default: 120 |

Examples:
Expand Down Expand Up @@ -1583,7 +1631,7 @@ Flags:
| Flag | Type | Description |
| --- | --- | --- |
| `--chat=<value>` | option | Chat selector (ID, local ID, title, or search text) Required. |
| `--level=<inbox|low>` | option | Destination: inbox (default mailbox) or low (Low Priority) Required. |
| `--level=<inbox\|low>` | option | Destination: inbox (default mailbox) or low (Low Priority) Required. |
| `--pick=<value>` | option | Pick the Nth result when the selector is ambiguous (1-indexed) |

Examples:
Expand Down Expand Up @@ -1863,12 +1911,12 @@ Flags:
| `--after=<value>` | option | Only messages at or after this ISO timestamp |
| `--before=<value>` | option | Only messages at or before this ISO timestamp |
| `--chat=<value>...` | option | Limit to a chat selector. Repeat for multiple. |
| `--chat-type=<group|single>` | option | Only group chats or direct messages |
| `--chat-type=<group\|single>` | option | Only group chats or direct messages |
| `--exclude-low-priority` | boolean | Exclude low-priority chats |
| `--ids` | boolean | Print only message IDs |
| `--include-muted` | boolean | Include muted chats |
| `--limit=<value>` | option | Maximum results Default: 50 |
| `--media=<any|video|image|link|file>...` | option | Filter by media type. Repeat for multiple. |
| `--media=<any\|video\|image\|link\|file>...` | option | Filter by media type. Repeat for multiple. |
| `--sender=<value>` | option | me, others, or a user ID |

Examples:
Expand Down Expand Up @@ -2201,7 +2249,7 @@ Flags:
| `--chat=<value>` | option | Chat selector (ID, local ID, title, or search text) Required. |
| `--duration=<value>` | option | When --state is typing, send paused automatically after this many seconds |
| `--pick=<value>` | option | Pick the Nth result when the selector is ambiguous (1-indexed) |
| `--state=<typing|paused>` | option | Indicator to send Default: typing |
| `--state=<typing\|paused>` | option | Indicator to send Default: typing |

Examples:

Expand Down Expand Up @@ -2367,8 +2415,8 @@ Flags:
| Flag | Type | Description |
| --- | --- | --- |
| `-c, --chat=<value>...` | option | Chat ID to subscribe to. Defaults to all chats. |
| `--exclude-type=<chat.upserted|chat.deleted|message.upserted|message.deleted>...` | option | Drop events of these types. Repeat for multiple. |
| `--include-type=<chat.upserted|chat.deleted|message.upserted|message.deleted>...` | option | Only forward events of these types. Repeat for multiple. |
| `--exclude-type=<chat.upserted\|chat.deleted\|message.upserted\|message.deleted>...` | option | Drop events of these types. Repeat for multiple. |
| `--include-type=<chat.upserted\|chat.deleted\|message.upserted\|message.deleted>...` | option | Only forward events of these types. Repeat for multiple. |
| `--webhook=<value>` | option | Forward each event to this URL as a POST request (best-effort, fire-and-forget) |
| `--webhook-queue=<value>` | option | Maximum pending webhook deliveries before dropping events Default: 64 |
| `--webhook-secret=<value>` | option | HMAC-SHA256 secret. Signs payloads with X-Beeper-Signature: sha256=<hex> |
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/bin/binary-bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void (async () => {
const payloadHash = createHash('sha256').update(archive).digest('hex').slice(0, 16)
const cacheRoot = process.env.BEEPER_CLI_BINARY_CACHE_DIR || join(homedir(), '.cache', 'beeper-cli', 'binary')
const payloadRoot = join(cacheRoot, payloadHash)
const entrypoint = join(payloadRoot, 'bin', 'run.js')
const entrypoint = join(payloadRoot, 'bin', 'cli.js')

if (!existsSync(entrypoint)) {
const tempArchive = join(tmpdir(), `beeper-cli-${payloadHash}.tar.gz`)
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bun
import { execute } from '@oclif/core'
import { renderStartupLogo } from './logo.js'

void (async () => {
if (process.argv.slice(2).length === 0 && process.env.BEEPER_NO_LOGO !== '1') {
process.stdout.write(`${renderStartupLogo()}\n\n`)
}

await execute({ dir: import.meta.url })
})()
Loading