Last active
January 15, 2026 04:35
-
-
Save R44VC0RP/bd391f6a23185c0fed6c6b5fb2bac50e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bun | |
| /** | |
| * ============================================================ | |
| * PROOF: Anthropic is specifically blocking "OpenCode" | |
| * in Claude Code OAuth system prompts | |
| * ============================================================ | |
| * | |
| * Video covering this script here: https://www.youtube.com/watch?v=G9YX6StP2-M | |
| * | |
| * This script demonstrates that Anthropic has specifically blocked | |
| * the phrase "You are OpenCode" in system prompts when using Claude's | |
| * OAuth tokens (Pro/Max subscription), while allowing other identity | |
| * statements like "You are Cursor", "You are Pi", etc. | |
| * | |
| * OpenCode (https://opencode.ai) is an open-source AI coding assistant. | |
| * | |
| * SETUP: | |
| * mkdir test-anthropic && cd test-anthropic | |
| * bun init -y | |
| * bun add @openauthjs/openauth | |
| * # Copy this file as opencode.ts | |
| * bun opencode.ts | |
| * | |
| * REQUIREMENTS: | |
| * - Bun runtime (https://bun.sh) | |
| * - Claude Pro or Max subscription | |
| * | |
| * HOW IT WORKS: | |
| * 1. Authenticates via OAuth using your Claude Pro/Max account | |
| * 2. Tests various system prompts against the Claude API | |
| * 3. Shows which prompts are blocked vs allowed | |
| * | |
| * FINDINGS: | |
| * - First system block MUST be exactly: | |
| * "You are Claude Code, Anthropic's official CLI for Claude." | |
| * - Second block can contain almost anything EXCEPT: | |
| * "You are OpenCode" or "You are opencode" | |
| * - Other identity statements work fine (Cursor, Pi, Droid, etc.) | |
| * | |
| * This appears to be targeted blocking of a specific competitor. | |
| */ | |
| import * as readline from "node:readline"; | |
| import { generatePKCE } from "@openauthjs/openauth/pkce"; | |
| const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"; | |
| const CC_HEADER = "You are Claude Code, Anthropic's official CLI for Claude."; | |
| const rl = readline.createInterface({ | |
| input: process.stdin, | |
| output: process.stdout, | |
| }); | |
| function prompt(question: string): Promise<string> { | |
| return new Promise((resolve) => { | |
| rl.question(question, (answer) => { | |
| resolve(answer); | |
| }); | |
| }); | |
| } | |
| // ============ OAuth Functions ============ | |
| async function authorize() { | |
| const pkce = await generatePKCE(); | |
| const url = new URL("https://claude.ai/oauth/authorize"); | |
| url.searchParams.set("code", "true"); | |
| url.searchParams.set("client_id", CLIENT_ID); | |
| url.searchParams.set("response_type", "code"); | |
| url.searchParams.set("redirect_uri", "https://console.anthropic.com/oauth/code/callback"); | |
| url.searchParams.set("scope", "org:create_api_key user:profile user:inference"); | |
| url.searchParams.set("code_challenge", pkce.challenge); | |
| url.searchParams.set("code_challenge_method", "S256"); | |
| url.searchParams.set("state", pkce.verifier); | |
| return { url: url.toString(), verifier: pkce.verifier }; | |
| } | |
| async function exchange(code: string, verifier: string) { | |
| const splits = code.split("#"); | |
| const result = await fetch("https://console.anthropic.com/v1/oauth/token", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ | |
| code: splits[0], | |
| state: splits[1], | |
| grant_type: "authorization_code", | |
| client_id: CLIENT_ID, | |
| redirect_uri: "https://console.anthropic.com/oauth/code/callback", | |
| code_verifier: verifier, | |
| }), | |
| }); | |
| if (!result.ok) return null; | |
| const json = await result.json(); | |
| return json.access_token; | |
| } | |
| // ============ Test Functions ============ | |
| interface TestResult { | |
| name: string; | |
| secondBlock: string; | |
| success: boolean; | |
| } | |
| async function runTest(accessToken: string, name: string, secondBlock: string): Promise<TestResult> { | |
| process.stdout.write(` ${name}... `); | |
| const response = await fetch("https://api.anthropic.com/v1/messages?beta=true", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| "Authorization": `Bearer ${accessToken}`, | |
| "anthropic-version": "2023-06-01", | |
| "anthropic-beta": "oauth-2025-04-20,interleaved-thinking-2025-05-14,claude-code-20250219", | |
| "user-agent": "claude-cli/2.1.2 (external, cli)", | |
| }, | |
| body: JSON.stringify({ | |
| model: "claude-sonnet-4-20250514", | |
| max_tokens: 1024, | |
| system: [ | |
| { type: "text", text: CC_HEADER }, | |
| { type: "text", text: secondBlock }, | |
| ], | |
| tools: [{ | |
| name: "mcp_test", | |
| description: "Test tool", | |
| input_schema: { type: "object", properties: {} }, | |
| }], | |
| messages: [{ role: "user", content: "Say hello in 5 words." }], | |
| }), | |
| }); | |
| if (!response.ok) { | |
| console.log("FAIL"); | |
| return { name, secondBlock, success: false }; | |
| } | |
| await response.json(); | |
| console.log("PASS"); | |
| return { name, secondBlock, success: true }; | |
| } | |
| async function runTests(accessToken: string) { | |
| console.log("\n" + "=".repeat(50)); | |
| console.log("SYSTEM PROMPT VALIDATION TESTS"); | |
| console.log("=".repeat(50)); | |
| console.log(`\nFirst block (required): "${CC_HEADER}"`); | |
| console.log("\nTesting second block content:\n"); | |
| const tests = [ | |
| // These FAIL - "You are OpenCode" pattern is blocked | |
| { name: "You are OpenCode.", block: "You are OpenCode." }, | |
| { name: "You are opencode.", block: "You are opencode." }, | |
| // These PASS - other identity statements work fine | |
| { name: "You are Cursor.", block: "You are Cursor." }, | |
| { name: "You are Pi.", block: "You are Pi." }, | |
| { name: "You are Droid.", block: "You are Droid." }, | |
| ]; | |
| const results: TestResult[] = []; | |
| for (const test of tests) { | |
| const result = await runTest(accessToken, test.name, test.block); | |
| results.push(result); | |
| await new Promise((r) => setTimeout(r, 300)); | |
| } | |
| // Summary | |
| console.log("\n" + "=".repeat(50)); | |
| console.log("RESULTS"); | |
| console.log("=".repeat(50)); | |
| const passed = results.filter((r) => r.success); | |
| const failed = results.filter((r) => !r.success); | |
| console.log(`\nPassed: ${passed.length} | Failed: ${failed.length}`); | |
| if (failed.length > 0) { | |
| console.log("\nBLOCKED (cannot use in 2nd system block):"); | |
| failed.forEach((r) => console.log(` - "${r.secondBlock}"`)); | |
| } | |
| if (passed.length > 0) { | |
| console.log("\nALLOWED:"); | |
| passed.forEach((r) => console.log(` - "${r.secondBlock}"`)); | |
| } | |
| console.log("\n" + "=".repeat(50)); | |
| console.log("CONCLUSION: Anthropic specifically blocks 'You are OpenCode'"); | |
| console.log(" but allows other identity statements."); | |
| console.log("=".repeat(50) + "\n"); | |
| } | |
| // ============ Main ============ | |
| async function main() { | |
| const args = process.argv.slice(2); | |
| console.log("\n" + "=".repeat(50)); | |
| console.log(" ANTHROPIC OAUTH SYSTEM PROMPT TESTER"); | |
| console.log("=".repeat(50)); | |
| let accessToken = args[0]; | |
| if (!accessToken) { | |
| console.log("\nNo token provided. Starting OAuth flow...\n"); | |
| const { url, verifier } = await authorize(); | |
| console.log("1. Open this URL in your browser:\n"); | |
| console.log(url); | |
| console.log("\n2. Authorize and copy the code shown.\n"); | |
| const code = await prompt("3. Paste the code here: "); | |
| console.log("\nExchanging code for token..."); | |
| accessToken = await exchange(code.trim(), verifier); | |
| if (!accessToken) { | |
| console.error("\nFailed to get access token!"); | |
| process.exit(1); | |
| } | |
| console.log("Got access token!\n"); | |
| } | |
| await runTests(accessToken); | |
| rl.close(); | |
| } | |
| main().catch(console.error).finally(() => rl.close()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment