apps/examples/desktop-app/README.md
Tauri desktop shell + Bun sidecar backend + Next.js UI for running and inspecting Cline chat sessions.
From apps/examples/desktop-app/:
bun run dev:web - Next.js UI only (http://localhost:3125)bun run dev:sidecar - sidecar backend onlybun run dev - Tauri desktop devbun run build - build web assetsbun run build:sidecar - build the Bun sidecar bundlebun run build:sidecar:bin - compile the Bun sidecar into a local binarybun run build:binary - build desktop binarybun run package:desktop - package the current OS desktop app into dist/desktop/bun run typecheck - TypeScript checkApps launched from Finder/the Dock inherit launchd's minimal PATH
(/usr/bin:/bin:/usr/sbin:/sbin), not the one your shell profiles build, so
agent-run commands would miss Homebrew-installed tools like gh even though
they work fine from a terminal. At startup the sidecar asks the user's login
shell — read from the account database via getpwuid, falling back to
$SHELL — for its PATH and merges it into process.env.PATH, which every
agent-spawned child (run_commands, MCP servers) inherits. Only PATH is
imported, deliberately; other login-environment variables (SSH_AUTH_SOCK,
API keys, JAVA_HOME-style tool roots) are not pulled in. Set
CLINE_SIDECAR_SKIP_SHELL_PATH=1 to disable. Implementation and details:
sidecar/shell-path.ts.
The framework-neutral color, typography, radius, and navigation contract lives
in the internal @cline/ui workspace
package. Other Cline web surfaces can take only its tokens or opt into the
Tailwind adapter and shared base styles without depending on the desktop
runtime. See webview/styles/README.md for the
desktop integration notes.
Releases are built, signed, notarized, and published by the desktop-publish
GitHub workflow. The step-by-step flow (version bumps, changelog, tag, repo
secrets) lives in the publish-desktop skill
(.cline/skills/publish-desktop/SKILL.md).
Installed apps auto-update via the Tauri updater: they poll the rolling
desktop-latest release's latest.json on launch and every 2 hours, install
updates in the background, and prompt for a restart. Two things must never be
lost: the desktop-latest release/tag (its feed URL is baked into shipped
apps) and the updater private key (TAURI_SIGNING_PRIVATE_KEY — without it,
shipped apps can't verify new updates).
Tauri desktop bundles are OS-specific, so build each package on the target OS:
bun run package:desktop:macbun run package:desktop:windowsbun run package:desktop:linuxThe macOS package script refuses to create a shareable package unless Developer ID signing and notarization credentials are configured. This prevents the common Gatekeeper failure where a downloaded unsigned build appears damaged on a teammate's Mac.
Set either APPLE_CERTIFICATE or APPLE_SIGNING_IDENTITY, plus one notarization credential set before packaging macOS:
APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_IDAPPLE_API_KEY or APPLE_API_KEY_PATH, APPLE_API_KEY_ID, APPLE_API_ISSUERFor local-only macOS testing, use bun run package:desktop:mac --allow-unsigned-mac. That ad-hoc signs the .app and strips quarantine attributes, but it is not suitable for a downloaded build shared with teammates.
One-time keychain setup:
.cer alone is not enough — you need the private key. If the admin generated the CSR, have them export the identity from Keychain Access as a .p12 and import it:
security import BeeCertificates.p12 -k ~/Library/Keychains/login.keychain-db -T /usr/bin/codesign -T /usr/bin/securitysecurity find-identity -v -p codesigning still reports 0 valid identities, the Apple intermediate CA is missing. Install it:
curl -O https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer && security import DeveloperIDG2CA.cer -k ~/Library/Keychains/login.keychain-dbsecurity find-identity -v -p codesigning — it should now list Developer ID Application: <Team Name> (<TEAMID>). That exact quoted string is your APPLE_SIGNING_IDENTITY.AuthKey_<KEYID>.p8 file, the Key ID, and the Issuer ID (a UUID from App Store Connect → Users and Access → Integrations). This is used for notarization only — nothing is published.Per-build:
export APPLE_SIGNING_IDENTITY="Developer ID Application: <Team Name> (<TEAMID>)"
export APPLE_API_KEY="<KEYID>" # Tauri reads APPLE_API_KEY (the Key ID); APPLE_API_KEY_ID alone silently skips notarization
export APPLE_API_KEY_PATH="/path/to/AuthKey_<KEYID>.p8"
export APPLE_API_ISSUER="<issuer UUID>"
bun run package:desktop:mac
The first signing run pops a keychain dialog — enter your macOS login password and click Always Allow. Notarization uploads the app to Apple's automated malware scan (typically 2–10 minutes) and staples the ticket. Artifacts land in dist/desktop/; share the .dmg. The DMG name takes its version from src-tauri/tauri.conf.json, the zip name from package.json — bump both.
Do not remove src-tauri/entitlements.plist or the bundle.macOS.entitlements reference in tauri.conf.json: notarization requires the hardened runtime, which breaks the Bun-compiled sidecar (SharedArrayBuffer is not defined, surfacing in-app as "desktop backend endpoint not ready") unless the JIT entitlements are present.
Startup flow:
/transport)
for desktop commands, queries, and pushed events.lib/desktop-client.ts and no longer imports @tauri-apps/api/core directly in feature code.workspaceRoot from git root and uses that same path as default cwd for chat runtime and git operations unless explicitly overridden.Desktop transport envelope:
{ "type": "command", "id": string, "command": string, "args"?: object }{ "type": "response", "id": string, "ok": boolean, "result"?: unknown, "error"?: string }{ "type": "event", "event": { "name": string, "payload": unknown } }Routine view for hub-backed automations.Routine lists all RPC schedules and shows status (enabled, nextRunAt, active execution).cline schedule through Tauri commands and scripts/routine-schedules.ts.src-tauri/src/main.rs - Tauri shell lifecycle, backend launch, and native-only commandssidecar/index.ts - persistent Bun sidecar and Hub-daemon entry dispatchsidecar/chat-session.ts - shared-Hub chat session adapterwebview/lib/desktop-client.ts - typed desktop websocket clientwebview/hooks/use-chat-session.ts - UI chat session state + backend subscriptionswebview/lib/chat-schema.ts - chat message schema used by the UIwebview/components/views/settings/routine-view.tsx - Routine schedules UI~/.cline/data/sessions/<sessionId>/ (or CLINE_SESSION_DATA_DIR).<sessionId>.messages.json.<sessionId>.messages.json is expected to contain ordered messages plus assistant modelInfo and metrics (including cache token fields when provided by the model runtime).<sessionId>.hooks.jsonl is observability/debug telemetry and should not be required for normal history replay/export flows.packages/core/docs/messages-contract-v1.md.The desktop sidecar sends SDK telemetry through the same configured OpenTelemetry
pipeline used by the CLI and writes structured runtime logs to
~/.cline/data/logs/code.log by default. Telemetry continues to honor the global
opt-out setting exposed in the desktop settings UI. The sidecar truncates stale
logs and rotates the active file before it exceeds 50 MiB.
Logging can be configured with the same environment variables as the CLI:
CLINE_LOG_ENABLED=0 disables file logging.CLINE_LOG_LEVEL sets the Pino level (for example, debug or warn).CLINE_LOG_PATH overrides the log destination.CLINE_LOG_NAME overrides the logger name.chat_event messages are arriving.finishReason=error before any assistant content is produced, the UI now adds an explicit error chat message so failed turns are visible in the transcript.bun run build:sdk).
The next desktop or CLI Hub connection will reuse a compatible running Hub or
replace an incompatible one through the shared discovery path.