From 1710246afbee692fa7f6c8dafa074a5267f741df Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Tue, 31 Mar 2026 18:44:23 +0200 Subject: [PATCH 01/22] fix: bypass GitHub app check for ultraplan/remote sessions Bake CCR_FORCE_BUNDLE=true into the build defines so the bundle-seed path is always active. This skips the GitHub remote + app installation checks that fail on non-Anthropic repos. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/build.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build.ts b/scripts/build.ts index 709fa1a51..262a6e9d6 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -141,6 +141,7 @@ const defines = { } : {}), 'process.env.CLAUDE_CODE_VERIFY_PLAN': JSON.stringify('false'), + 'process.env.CCR_FORCE_BUNDLE': JSON.stringify('true'), 'MACRO.VERSION': JSON.stringify(version), 'MACRO.BUILD_TIME': JSON.stringify(buildTime), 'MACRO.PACKAGE_URL': JSON.stringify(pkg.name), From b1638352136b85b825a180328ad120a9481f028e Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Tue, 31 Mar 2026 18:48:12 +0200 Subject: [PATCH 02/22] docs: add IPFS mirror details to README Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 285257216..da41fe2c1 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,17 @@ src/ --- +## IPFS Mirror + +A full copy of this repository is permanently pinned on IPFS via Filecoin: + +- **CID:** `bafybeiegvef3dt24n2znnnmzcud2vxat7y7rl5ikz7y7yoglxappim54bm` +- **Gateway:** https://w3s.link/ipfs/bafybeiegvef3dt24n2znnnmzcud2vxat7y7rl5ikz7y7yoglxappim54bm + +If this repo gets taken down, the code lives on. + +--- + ## License The original Claude Code source is the property of Anthropic. This fork exists because the source was publicly exposed through their npm distribution. Use at your own discretion. From 95a56f58bab8079a7a8d738ca82e995a342c3b62 Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Tue, 31 Mar 2026 19:04:40 +0200 Subject: [PATCH 03/22] chore: remove outdated app_structure_llm.txt Co-Authored-By: Claude Opus 4.6 (1M context) --- app_structure_llm.txt | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 app_structure_llm.txt diff --git a/app_structure_llm.txt b/app_structure_llm.txt deleted file mode 100644 index c0165625b..000000000 --- a/app_structure_llm.txt +++ /dev/null @@ -1,21 +0,0 @@ -# App Structure - Claude Code Source Snapshot -# Last updated: 2026-03-31 - -## Repositories -- GitHub: https://github.com/paoloanzn/claude-code - -## Backend (Supabase) -- N/A - -## Deployed Services (Railway) -- N/A - -## Environment Variables Required -- USER_TYPE (optional; external/public build defaults to non-ant behavior) -- CLAUDE_CODE_ENABLE_CFC (optional) -- CLAUDE_CODE_REMOTE (optional) - -## Notes -- This repository is a reconstructed Bun CLI workspace built from a leaked TypeScript source snapshot. -- The source build targets `src/entrypoints/cli.tsx` and emits `cli.js` via `bun run build`. -- Anthropic-internal `@ant/*` packages are treated as optional/external. Chrome integration is lazy-loaded so the default CLI startup path does not fail when those packages are unavailable. From ecc2d6857801d6e1a9a3758d77459963371cc2d4 Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Tue, 31 Mar 2026 19:20:32 +0200 Subject: [PATCH 04/22] feat: add one-liner install script curl | bash installer that checks OS, git, bun, clones, builds with all experimental features, and symlinks as `free-code` on PATH. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 16 +++++ install.sh | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100755 install.sh diff --git a/README.md b/README.md index da41fe2c1..8b384371e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,22 @@ See [FEATURES.md](FEATURES.md) for the full audit of all 88 flags and their stat --- +## Quick install + +```bash +curl -fsSL https://raw.githubusercontent.com/paoloanzn/free-code/main/install.sh | bash +``` + +This will check your system, install Bun if needed, clone the repo, build the binary with all experimental features enabled, and symlink it as `free-code` on your PATH. + +After install, just run: +```bash +export ANTHROPIC_API_KEY="sk-ant-..." +free-code +``` + +--- + ## Requirements - [Bun](https://bun.sh) >= 1.3.11 diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..3a632e882 --- /dev/null +++ b/install.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash +set -euo pipefail + +# free-code installer +# Usage: curl -fsSL https://raw.githubusercontent.com/paoloanzn/free-code/main/install.sh | bash + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +DIM='\033[2m' +RESET='\033[0m' + +REPO="https://github.com/paoloanzn/free-code.git" +INSTALL_DIR="$HOME/free-code" +BUN_MIN_VERSION="1.3.11" + +info() { printf "${CYAN}[*]${RESET} %s\n" "$*"; } +ok() { printf "${GREEN}[+]${RESET} %s\n" "$*"; } +warn() { printf "${YELLOW}[!]${RESET} %s\n" "$*"; } +fail() { printf "${RED}[x]${RESET} %s\n" "$*"; exit 1; } + +header() { + echo "" + printf "${BOLD}${CYAN}" + cat << 'ART' + ___ _ + / _|_ __ ___ ___ ___ __| | ___ + | |_| '__/ _ \/ _ \_____ / __/ _` |/ _ \ + | _| | | __/ __/_____| (_| (_| | __/ + |_| |_| \___|\___| \___\__,_|\___| + +ART + printf "${RESET}" + printf "${DIM} The free build of Claude Code${RESET}\n" + echo "" +} + +# ------------------------------------------------------------------- +# System checks +# ------------------------------------------------------------------- + +check_os() { + case "$(uname -s)" in + Darwin) OS="macos" ;; + Linux) OS="linux" ;; + *) fail "Unsupported OS: $(uname -s). macOS or Linux required." ;; + esac + ok "OS: $(uname -s) $(uname -m)" +} + +check_git() { + if ! command -v git &>/dev/null; then + fail "git is not installed. Install it first: + macOS: xcode-select --install + Linux: sudo apt install git (or your distro's equivalent)" + fi + ok "git: $(git --version | head -1)" +} + +# Compare semver: returns 0 if $1 >= $2 +version_gte() { + [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" = "$2" ] +} + +check_bun() { + if command -v bun &>/dev/null; then + local ver + ver="$(bun --version 2>/dev/null || echo "0.0.0")" + if version_gte "$ver" "$BUN_MIN_VERSION"; then + ok "bun: v${ver}" + return + fi + warn "bun v${ver} found but v${BUN_MIN_VERSION}+ required. Upgrading..." + else + info "bun not found. Installing..." + fi + install_bun +} + +install_bun() { + curl -fsSL https://bun.sh/install | bash + # Source the updated profile so bun is on PATH for this session + export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}" + export PATH="$BUN_INSTALL/bin:$PATH" + if ! command -v bun &>/dev/null; then + fail "bun installation succeeded but binary not found on PATH. + Add this to your shell profile and restart: + export PATH=\"\$HOME/.bun/bin:\$PATH\"" + fi + ok "bun: v$(bun --version) (just installed)" +} + +# ------------------------------------------------------------------- +# Clone & build +# ------------------------------------------------------------------- + +clone_repo() { + if [ -d "$INSTALL_DIR" ]; then + warn "$INSTALL_DIR already exists" + if [ -d "$INSTALL_DIR/.git" ]; then + info "Pulling latest changes..." + git -C "$INSTALL_DIR" pull --ff-only origin main 2>/dev/null || { + warn "Pull failed, continuing with existing copy" + } + fi + else + info "Cloning repository..." + git clone --depth 1 "$REPO" "$INSTALL_DIR" + fi + ok "Source: $INSTALL_DIR" +} + +install_deps() { + info "Installing dependencies..." + cd "$INSTALL_DIR" + bun install --frozen-lockfile 2>/dev/null || bun install + ok "Dependencies installed" +} + +build_binary() { + info "Building free-code (all experimental features enabled)..." + cd "$INSTALL_DIR" + bun run build:dev:full + ok "Binary built: $INSTALL_DIR/cli-dev" +} + +link_binary() { + local link_dir="$HOME/.local/bin" + mkdir -p "$link_dir" + + ln -sf "$INSTALL_DIR/cli-dev" "$link_dir/free-code" + ok "Symlinked: $link_dir/free-code" + + if ! echo "$PATH" | tr ':' '\n' | grep -qx "$link_dir"; then + warn "$link_dir is not on your PATH" + echo "" + printf "${YELLOW} Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.):${RESET}\n" + printf "${BOLD} export PATH=\"\$HOME/.local/bin:\$PATH\"${RESET}\n" + echo "" + fi +} + +# ------------------------------------------------------------------- +# Main +# ------------------------------------------------------------------- + +header +info "Starting installation..." +echo "" + +check_os +check_git +check_bun +echo "" + +clone_repo +install_deps +build_binary +link_binary + +echo "" +printf "${GREEN}${BOLD} Installation complete!${RESET}\n" +echo "" +printf " ${BOLD}Run it:${RESET}\n" +printf " ${CYAN}free-code${RESET} # interactive REPL\n" +printf " ${CYAN}free-code -p \"your prompt\"${RESET} # one-shot mode\n" +echo "" +printf " ${BOLD}Set your API key:${RESET}\n" +printf " ${CYAN}export ANTHROPIC_API_KEY=\"sk-ant-...\"${RESET}\n" +echo "" +printf " ${BOLD}Or log in with Claude.ai:${RESET}\n" +printf " ${CYAN}free-code /login${RESET}\n" +echo "" +printf " ${DIM}Source: $INSTALL_DIR${RESET}\n" +printf " ${DIM}Binary: $INSTALL_DIR/cli-dev${RESET}\n" +printf " ${DIM}Link: ~/.local/bin/free-code${RESET}\n" +echo "" From e5b13b347c0f06b3e0e561ed83d9e15caf8d023a Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Tue, 31 Mar 2026 19:22:37 +0200 Subject: [PATCH 05/22] docs: move install one-liner to top of README Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8b384371e..11713ecf1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ All telemetry stripped. All injected security-prompt guardrails removed. All experimental features unlocked. One binary, zero callbacks home. +```bash +curl -fsSL https://raw.githubusercontent.com/paoloanzn/free-code/main/install.sh | bash +``` + +> Checks your system, installs Bun if needed, clones, builds with all features enabled, and puts `free-code` on your PATH. Then just `export ANTHROPIC_API_KEY="sk-ant-..."` and run `free-code`. +

free-code screenshot

From a80fc1043d3dfe5844b98e5addc6f134c4a75a1e Mon Sep 17 00:00:00 2001 From: Muhammad Abdul Rehman Date: Wed, 1 Apr 2026 02:46:56 +0500 Subject: [PATCH 06/22] feat: complete Codex API feature parity with UI updates --- .gitignore | 1 + CLAUDE.md | 47 ++ src/cli/handlers/auth.ts | 23 +- src/components/ConsoleOAuthFlow.tsx | 232 +++++--- src/constants/codex-oauth.ts | 44 ++ src/entrypoints/cli.tsx | 10 + src/hooks/useApiKeyVerification.ts | 5 +- src/services/api/client.ts | 18 + src/services/api/codex-fetch-adapter.ts | 762 ++++++++++++++++++++++++ src/services/oauth/codex-client.ts | 394 ++++++++++++ src/utils/auth.ts | 66 ++ src/utils/config.ts | 12 + src/utils/logoV2Utils.ts | 6 +- src/utils/model/model.ts | 22 + src/utils/model/validateModel.ts | 8 + src/utils/ripgrep.ts | 4 +- src/vendor/ripgrep/x64-linux/rg | Bin 0 -> 6590000 bytes 17 files changed, 1553 insertions(+), 101 deletions(-) create mode 100644 CLAUDE.md create mode 100644 src/constants/codex-oauth.ts create mode 100644 src/services/api/codex-fetch-adapter.ts create mode 100644 src/services/oauth/codex-client.ts create mode 100755 src/vendor/ripgrep/x64-linux/rg diff --git a/.gitignore b/.gitignore index 00128ff05..e17a38369 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ dist/ cli cli-dev +openclaw/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..43a426911 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,47 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Common commands + +```bash +# Install dependencies +bun install + +# Standard build (./cli) +bun run build + +# Dev build (./cli-dev) +bun run build:dev + +# Dev build with all experimental features (./cli-dev) +bun run build:dev:full + +# Compiled build (./dist/cli) +bun run compile + +# Run from source without compiling +bun run dev +``` + +Run the built binary with `./cli` or `./cli-dev`. Set `ANTHROPIC_API_KEY` in the environment or use OAuth via `./cli /login`. + +## High-level architecture + +- **Entry point/UI loop**: src/entrypoints/cli.tsx bootstraps the CLI, with the main interactive UI in src/screens/REPL.tsx (Ink/React). +- **Command/tool registries**: src/commands.ts registers slash commands; src/tools.ts registers tool implementations. Implementations live in src/commands/ and src/tools/. +- **LLM query pipeline**: src/QueryEngine.ts coordinates message flow, tool use, and model invocation. +- **Core subsystems**: + - src/services/: API clients, OAuth/MCP integration, analytics stubs + - src/state/: app state store + - src/hooks/: React hooks used by UI/flows + - src/components/: terminal UI components (Ink) + - src/skills/: skill system + - src/plugins/: plugin system + - src/bridge/: IDE bridge + - src/voice/: voice input + - src/tasks/: background task management + +## Build system + +- scripts/build.ts is the build script and feature-flag bundler. Feature flags are set via build arguments (e.g., `--feature=ULTRAPLAN`) or presets like `--feature-set=dev-full` (see README for details). \ No newline at end of file diff --git a/src/cli/handlers/auth.ts b/src/cli/handlers/auth.ts index c4cba5d06..15b0bb4f5 100644 --- a/src/cli/handlers/auth.ts +++ b/src/cli/handlers/auth.ts @@ -27,6 +27,7 @@ import { getOauthAccountInfo, getSubscriptionType, isUsing3PServices, + saveCodexOAuthTokens, saveOAuthTokensIfNeeded, validateForceLoginOrg, } from '../../utils/auth.js' @@ -43,6 +44,16 @@ import { buildAPIProviderProperties, } from '../../utils/status.js' +/** + * Returns true if the token carries any Anthropic-issued scope (user:* or org:*). + * Codex tokens use OpenID Connect scopes (openid, profile, email, offline_access) + * which are not Anthropic scopes, so this returns false for them. + */ +function hasAnyAnthropicScope(scopes: string[] | undefined): boolean { + if (!scopes?.length) return false + return scopes.some((s) => s.startsWith('user:') || s.startsWith('org:')) +} + /** * Shared post-token-acquisition logic. Saves tokens, fetches profile/roles, * and sets up the local auth state. @@ -96,7 +107,7 @@ export async function installOAuthTokens(tokens: OAuthTokens): Promise { await fetchAndStoreClaudeCodeFirstTokenDate().catch(err => logForDebugging(String(err), { level: 'error' }), ) - } else { + } else if (hasAnyAnthropicScope(tokens.scopes)) { // API key creation is critical for Console users — let it throw. const apiKey = await createAndStoreApiKey(tokens.accessToken) if (!apiKey) { @@ -104,6 +115,16 @@ export async function installOAuthTokens(tokens: OAuthTokens): Promise { 'Unable to create API key. The server accepted the request but did not return a key.', ) } + } else { + // Third-party provider (e.g. OpenAI Codex) — tokens carry no Anthropic + // scopes. Skip Anthropic API key creation entirely and store the tokens + // in their own dedicated config slot. + saveCodexOAuthTokens({ + accessToken: tokens.accessToken, + refreshToken: tokens.refreshToken ?? '', + expiresAt: tokens.expiresAt ?? Date.now() + 3600_000, + accountId: (tokens.tokenAccount?.uuid ?? ''), + }) } await clearAuthRelatedCaches() diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 717697f68..abbae9228 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -9,8 +9,9 @@ import { Box, Link, Text } from '../ink.js'; import { useKeybinding } from '../keybindings/useKeybinding.js'; import { getSSLErrorHint } from '../services/api/errorUtils.js'; import { sendNotification } from '../services/notifier.js'; +import { runCodexOAuthFlow } from '../services/oauth/codex-client.js'; import { OAuthService } from '../services/oauth/index.js'; -import { getOauthAccountInfo, validateForceLoginOrg } from '../utils/auth.js'; +import { getOauthAccountInfo, saveCodexOAuthTokens, validateForceLoginOrg } from '../utils/auth.js'; import { logError } from '../utils/log.js'; import { getSettings_DEPRECATED } from '../utils/settings/settings.js'; import { Select } from './CustomSelect/select.js'; @@ -84,6 +85,7 @@ export function ConsoleOAuthFlow({ // Use Claude AI auth for setup-token mode to support user:inference scope return mode === 'setup-token' || forceLoginMethod === 'claudeai'; }); + const [loginWithCodex, setLoginWithCodex] = useState(false); // After a few seconds we suggest the user to copy/paste url if the // browser did not open automatically. In this flow we expect the user to // copy the code from the browser and paste it in the terminal @@ -235,7 +237,7 @@ export function ConsoleOAuthFlow({ await installOAuthTokens(result); const orgResult = await validateForceLoginOrg(); if (!orgResult.valid) { - throw new Error(orgResult.message); + throw new Error('message' in orgResult ? (orgResult as any).message : 'Invalid organization'); } setOAuthStatus({ state: 'success' @@ -261,16 +263,46 @@ export function ConsoleOAuthFlow({ }); } }, [oauthService, setShowPastePrompt, loginWithClaudeAi, mode, orgUUID]); + + // Codex-specific OAuth flow — completely separate from the Anthropic OAuthService + const startCodexOAuth = useCallback(async () => { + try { + logEvent('tengu_oauth_codex_flow_start', {}); + const codexTokens = await runCodexOAuthFlow(async (url) => { + setOAuthStatus({ state: 'waiting_for_login', url }); + setTimeout(setShowPastePrompt, 3000, true); + }); + // Save directly via saveCodexOAuthTokens (bypasses installOAuthTokens Anthropic path) + saveCodexOAuthTokens(codexTokens); + logEvent('tengu_oauth_codex_success', {}); + setOAuthStatus({ state: 'success' }); + void sendNotification({ message: 'Codex login successful', notificationType: 'auth_success' }, terminal); + } catch (err) { + const msg = (err as Error).message; + logEvent('tengu_oauth_codex_error', { + error: msg as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }); + setOAuthStatus({ state: 'error', message: msg, toRetry: { state: 'idle' } }); + } + }, [setShowPastePrompt, terminal]); + const pendingOAuthStartRef = useRef(false); useEffect(() => { if (oauthStatus.state === 'ready_to_start' && !pendingOAuthStartRef.current) { pendingOAuthStartRef.current = true; - process.nextTick((startOAuth_0: () => Promise, pendingOAuthStartRef_0: React.MutableRefObject) => { - void startOAuth_0(); - pendingOAuthStartRef_0.current = false; - }, startOAuth, pendingOAuthStartRef); + if (loginWithCodex) { + process.nextTick((startCodexOAuth_0: () => Promise, pendingOAuthStartRef_0: React.MutableRefObject) => { + void startCodexOAuth_0(); + pendingOAuthStartRef_0.current = false; + }, startCodexOAuth, pendingOAuthStartRef); + } else { + process.nextTick((startOAuth_0: () => Promise, pendingOAuthStartRef_0: React.MutableRefObject) => { + void startOAuth_0(); + pendingOAuthStartRef_0.current = false; + }, startOAuth, pendingOAuthStartRef); + } } - }, [oauthStatus.state, startOAuth]); + }, [oauthStatus.state, startOAuth, startCodexOAuth, loginWithCodex]); // Auto-exit for setup-token mode useEffect(() => { @@ -325,7 +357,7 @@ export function ConsoleOAuthFlow({ } - + ; } @@ -343,9 +375,10 @@ type OAuthStatusMessageProps = { handleSubmitCode: (value: string, url: string) => void; setOAuthStatus: (status: OAuthStatus) => void; setLoginWithClaudeAi: (value: boolean) => void; + setLoginWithCodex: (value: boolean) => void; }; function OAuthStatusMessage(t0) { - const $ = _c(51); + const $ = _c(52); const { oauthStatus, mode, @@ -359,7 +392,8 @@ function OAuthStatusMessage(t0) { textInputColumns, handleSubmitCode, setOAuthStatus, - setLoginWithClaudeAi + setLoginWithClaudeAi, + setLoginWithCodex } = t0; switch (oauthStatus.state) { case "idle": @@ -405,20 +439,29 @@ function OAuthStatusMessage(t0) { t6 = [t4, t5, { label: 3rd-party platform ·{" "}Amazon Bedrock, Microsoft Foundry, or Vertex AI{"\n"}, value: "platform" + }, { + label: OpenAI Codex account ·{" "}ChatGPT Plus/Pro subscription{"\n"}, + value: "codex" }]; $[5] = t6; } else { t6 = $[5]; } let t7; - if ($[6] !== setLoginWithClaudeAi || $[7] !== setOAuthStatus) { + if ($[6] !== setLoginWithClaudeAi || $[7] !== setOAuthStatus || $[8] !== setLoginWithCodex) { t7 =