Skip to content
Open
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
22 changes: 22 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,25 @@ npm version patch --no-git-tag-version # 0.0.27 → 0.0.28
- Git worktree per chat (isolation)
- Claude Code execution in worktree path
- Full feature parity with web app

## Debug Mode

When debugging runtime issues in the renderer or main process, use the structured debug logging system. This avoids asking the user to manually copy-paste console output.

**Start the server:**
```bash
bun packages/debug/src/server.ts &
```

**Instrument renderer code** (no import needed, fails silently):
```js
fetch('http://localhost:7799/log',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({tag:'TAG',msg:'MESSAGE',data:{},ts:Date.now()})}).catch(()=>{});
```

**Read logs:** Read `.debug/logs.ndjson` - each line is a JSON object with `tag`, `msg`, `data`, `ts`.

**Clear logs:** `curl -X DELETE http://localhost:7799/logs`

**Workflow:** Hypothesize → instrument → user reproduces → read logs → fix with evidence → verify → remove instrumentation.

See `packages/debug/INSTRUCTIONS.md` for the full protocol.
283 changes: 196 additions & 87 deletions bun.lock

Large diffs are not rendered by default.

Binary file modified bun.lockb
Binary file not shown.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "21st-desktop",
"version": "0.0.54",
"version": "0.0.59",
"private": true,
"description": "1Code - UI for parallel work with AI agents",
"author": {
Expand All @@ -19,8 +19,8 @@
"dist": "electron-builder",
"dist:manifest": "node scripts/generate-update-manifest.mjs",
"dist:upload": "node scripts/upload-release.mjs",
"claude:download": "node scripts/download-claude-binary.mjs --version=2.1.25",
"claude:download:all": "node scripts/download-claude-binary.mjs --version=2.1.25 --all",
"claude:download": "node scripts/download-claude-binary.mjs --version=2.1.32",
"claude:download:all": "node scripts/download-claude-binary.mjs --version=2.1.32 --all",
"release": "rm -rf release && bun i && bun run claude:download && bun run build && bun run package:mac && bun run dist:manifest && ./scripts/upload-release-wrangler.sh",
"release:dev": "rm -rf release && bun run claude:download && bun run build && bun run package:mac && rm -rf node_modules && bun i",
"sync:public": "./scripts/sync-to-public.sh",
Expand All @@ -33,7 +33,7 @@
},
"dependencies": {
"@ai-sdk/react": "^3.0.14",
"@anthropic-ai/claude-agent-sdk": "0.2.25",
"@anthropic-ai/claude-agent-sdk": "0.2.32",
"@git-diff-view/react": "^0.0.35",
"@git-diff-view/shiki": "^0.0.36",
"@modelcontextprotocol/sdk": "^1.25.3",
Expand Down
25 changes: 14 additions & 11 deletions src/main/lib/auto-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,21 @@ function getChannelPrefPath(): string {
}

function getSavedChannel(): UpdateChannel {
try {
const prefPath = getChannelPrefPath()
if (existsSync(prefPath)) {
const data = JSON.parse(readFileSync(prefPath, "utf-8"))
if (data.channel === "beta" || data.channel === "latest") {
return data.channel
}
}
} catch {
// Ignore read errors, fall back to default
}
// Beta channel disabled until beta-mac.yml is published to CDN.
// Always use "latest" to prevent 404 errors for users who toggled Early Access.
return "latest"
// try {
// const prefPath = getChannelPrefPath()
// if (existsSync(prefPath)) {
// const data = JSON.parse(readFileSync(prefPath, "utf-8"))
// if (data.channel === "beta" || data.channel === "latest") {
// return data.channel
// }
// }
// } catch {
// // Ignore read errors, fall back to default
// }
// return "latest"
}

function saveChannel(channel: UpdateChannel): void {
Expand Down
48 changes: 29 additions & 19 deletions src/main/lib/claude/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,22 @@ let cachedShellEnv: Record<string, string> | null = null
const DELIMITER = "_CLAUDE_ENV_DELIMITER_"

// Keys to strip (prevent interference from unrelated providers)
// NOTE: We intentionally keep ANTHROPIC_API_KEY and ANTHROPIC_BASE_URL
// NOTE: We intentionally keep ANTHROPIC_API_KEY and ANTHROPIC_BASE_URL in production
// so users can use their existing Claude Code CLI configuration (API proxy, etc.)
// Based on PR #29 by @sa4hnd
const STRIPPED_ENV_KEYS = [
const STRIPPED_ENV_KEYS_BASE = [
"OPENAI_API_KEY",
"CLAUDE_CODE_USE_BEDROCK",
"CLAUDE_CODE_USE_VERTEX",
]

// In dev mode, also strip ANTHROPIC_API_KEY so OAuth token is used instead
// This allows devs to test OAuth flow without unsetting their shell env
// Added by Sergey Bunas for dev purposes
const STRIPPED_ENV_KEYS = !app.isPackaged
? [...STRIPPED_ENV_KEYS_BASE, "ANTHROPIC_API_KEY"]
: STRIPPED_ENV_KEYS_BASE

// Cache the bundled binary path (only compute once)
let cachedBinaryPath: string | null = null
let binaryPathComputed = false
Expand All @@ -45,14 +52,12 @@ export function getBundledClaudeBinaryPath(): string {
const currentPlatform = process.platform
const arch = process.arch

// Only log verbose info on first call
if (process.env.DEBUG_CLAUDE_BINARY) {
console.log("[claude-binary] ========== BUNDLED BINARY PATH ==========")
console.log("[claude-binary] isDev:", isDev)
console.log("[claude-binary] platform:", currentPlatform)
console.log("[claude-binary] arch:", arch)
console.log("[claude-binary] appPath:", app.getAppPath())
}
// Always log on first call to help debug
console.log("[claude-binary] ========== BUNDLED BINARY DEBUG ==========")
console.log("[claude-binary] isDev:", isDev)
console.log("[claude-binary] platform:", currentPlatform)
console.log("[claude-binary] arch:", arch)
console.log("[claude-binary] appPath:", app.getAppPath())

// In dev: apps/desktop/resources/bin/{platform}-{arch}/claude
// In production: {resourcesPath}/bin/claude
Expand All @@ -64,21 +69,16 @@ export function getBundledClaudeBinaryPath(): string {
)
: path.join(process.resourcesPath, "bin")

if (process.env.DEBUG_CLAUDE_BINARY) {
console.log("[claude-binary] resourcesPath:", resourcesPath)
}
console.log("[claude-binary] resourcesPath:", resourcesPath)

const binaryName = currentPlatform === "win32" ? "claude.exe" : "claude"
const binaryPath = path.join(resourcesPath, binaryName)

if (process.env.DEBUG_CLAUDE_BINARY) {
console.log("[claude-binary] binaryPath:", binaryPath)
}
console.log("[claude-binary] binaryPath:", binaryPath)

// Check if binary exists
const exists = fs.existsSync(binaryPath)

// Always log if binary doesn't exist (critical error)
if (!exists) {
console.error(
"[claude-binary] WARNING: Binary not found at path:",
Expand All @@ -87,15 +87,15 @@ export function getBundledClaudeBinaryPath(): string {
console.error(
"[claude-binary] Run 'bun run claude:download' to download it"
)
} else if (process.env.DEBUG_CLAUDE_BINARY) {
} else {
const stats = fs.statSync(binaryPath)
const sizeMB = (stats.size / 1024 / 1024).toFixed(1)
const isExecutable = (stats.mode & fs.constants.X_OK) !== 0
console.log("[claude-binary] exists:", exists)
console.log("[claude-binary] size:", sizeMB, "MB")
console.log("[claude-binary] isExecutable:", isExecutable)
console.log("[claude-binary] ===========================================")
}
console.log("[claude-binary] ============================================")

// Cache the result
cachedBinaryPath = binaryPath
Expand Down Expand Up @@ -237,6 +237,16 @@ export function buildClaudeEnv(options?: {
env.PATH = shellPath
}

// 2b. Strip sensitive keys again (process.env may have re-added them)
// This ensures ANTHROPIC_API_KEY from dev's shell doesn't override OAuth in dev mode
// Added by Sergey Bunas for dev purposes
for (const key of STRIPPED_ENV_KEYS) {
if (key in env) {
console.log(`[claude-env] Stripped ${key} from final environment`)
delete env[key]
}
}

// 3. Ensure critical vars are present using platform provider
const platformEnv = platform.buildEnvironment()
if (!env.HOME) env.HOME = platformEnv.HOME
Expand Down
Loading