feat: rebrand as Open Claude and harden OpenAI REPL
This commit is contained in:
@@ -52,8 +52,11 @@ const result = await Bun.build({
|
||||
naming: 'cli.mjs',
|
||||
define: {
|
||||
// MACRO.* build-time constants
|
||||
// Set version high enough to pass minimum version checks
|
||||
// Keep the internal compatibility version high enough to pass
|
||||
// first-party minimum-version guards, but expose the real package
|
||||
// version separately in Open Claude branding.
|
||||
'MACRO.VERSION': JSON.stringify('99.0.0'),
|
||||
'MACRO.DISPLAY_VERSION': JSON.stringify(version),
|
||||
'MACRO.BUILD_TIME': JSON.stringify(new Date().toISOString()),
|
||||
'MACRO.ISSUES_EXPLAINER':
|
||||
JSON.stringify('report the issue at https://github.com/anthropics/claude-code/issues'),
|
||||
|
||||
@@ -33,42 +33,42 @@ type Segments = {
|
||||
};
|
||||
const POSES: Record<ClawdPose, Segments> = {
|
||||
default: {
|
||||
r1L: ' ▐',
|
||||
r1E: '▛███▜',
|
||||
r1R: '▌',
|
||||
r2L: '▝▜',
|
||||
r2R: '▛▘'
|
||||
r1L: ' ╭',
|
||||
r1E: '○ ○ ',
|
||||
r1R: '╮',
|
||||
r2L: ' │',
|
||||
r2R: '│ '
|
||||
},
|
||||
'look-left': {
|
||||
r1L: ' ▐',
|
||||
r1E: '▟███▟',
|
||||
r1R: '▌',
|
||||
r2L: '▝▜',
|
||||
r2R: '▛▘'
|
||||
r1L: ' ╭',
|
||||
r1E: '◔ ○ ',
|
||||
r1R: '╮',
|
||||
r2L: ' │',
|
||||
r2R: '│ '
|
||||
},
|
||||
'look-right': {
|
||||
r1L: ' ▐',
|
||||
r1E: '▙███▙',
|
||||
r1R: '▌',
|
||||
r2L: '▝▜',
|
||||
r2R: '▛▘'
|
||||
r1L: ' ╭',
|
||||
r1E: '○ ◔ ',
|
||||
r1R: '╮',
|
||||
r2L: ' │',
|
||||
r2R: '│ '
|
||||
},
|
||||
'arms-up': {
|
||||
r1L: '▗▟',
|
||||
r1E: '▛███▜',
|
||||
r1R: '▙▖',
|
||||
r2L: ' ▜',
|
||||
r2R: '▛ '
|
||||
r1L: '\\╭',
|
||||
r1E: '○ ○ ',
|
||||
r1R: '╮/',
|
||||
r2L: ' │',
|
||||
r2R: '│ '
|
||||
}
|
||||
};
|
||||
|
||||
// Apple Terminal uses a bg-fill trick (see below), so only eye poses make
|
||||
// sense. Arm poses fall back to default.
|
||||
const APPLE_EYES: Record<ClawdPose, string> = {
|
||||
default: ' ▗ ▖ ',
|
||||
'look-left': ' ▘ ▘ ',
|
||||
'look-right': ' ▝ ▝ ',
|
||||
'arms-up': ' ▗ ▖ '
|
||||
default: ' ○ ○ ',
|
||||
'look-left': ' ◔ ○ ',
|
||||
'look-right': ' ○ ◔ ',
|
||||
'arms-up': ' ○ ○ '
|
||||
};
|
||||
export function Clawd(t0) {
|
||||
const $ = _c(26);
|
||||
@@ -140,7 +140,7 @@ export function Clawd(t0) {
|
||||
}
|
||||
let t8;
|
||||
if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t8 = <Text color="clawd_body" backgroundColor="clawd_background">█████</Text>;
|
||||
t8 = <Text color="clawd_body" backgroundColor="clawd_background">OPEN </Text>;
|
||||
$[16] = t8;
|
||||
} else {
|
||||
t8 = $[16];
|
||||
@@ -164,7 +164,7 @@ export function Clawd(t0) {
|
||||
}
|
||||
let t11;
|
||||
if ($[22] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t11 = <Text color="clawd_body">{" "}▘▘ ▝▝{" "}</Text>;
|
||||
t11 = <Text color="clawd_body">{" "}╰─◡─╯{" "}</Text>;
|
||||
$[22] = t11;
|
||||
} else {
|
||||
t11 = $[22];
|
||||
|
||||
@@ -88,7 +88,7 @@ export function CondensedLogo() {
|
||||
}
|
||||
let t5;
|
||||
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t5 = <Text bold={true}>Claude Code</Text>;
|
||||
t5 = <Text bold={true}>Open Claude</Text>;
|
||||
$[8] = t5;
|
||||
} else {
|
||||
t5 = $[8];
|
||||
|
||||
@@ -28,7 +28,7 @@ export function WelcomeV2() {
|
||||
let t7;
|
||||
let t8;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Text><Text color="claude">{"Welcome to Claude Code"} </Text><Text dimColor={true}>v{MACRO.VERSION} </Text></Text>;
|
||||
t0 = <Text><Text color="claude">{"Welcome to Open Claude"} </Text><Text dimColor={true}>v{MACRO.DISPLAY_VERSION ?? MACRO.VERSION} </Text></Text>;
|
||||
t1 = <Text>{"\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026"}</Text>;
|
||||
t2 = <Text>{" "}</Text>;
|
||||
t3 = <Text>{" "}</Text>;
|
||||
@@ -113,7 +113,7 @@ export function WelcomeV2() {
|
||||
let t5;
|
||||
let t6;
|
||||
if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Text><Text color="claude">{"Welcome to Claude Code"} </Text><Text dimColor={true}>v{MACRO.VERSION} </Text></Text>;
|
||||
t0 = <Text><Text color="claude">{"Welcome to Open Claude"} </Text><Text dimColor={true}>v{MACRO.DISPLAY_VERSION ?? MACRO.VERSION} </Text></Text>;
|
||||
t1 = <Text>{"\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026"}</Text>;
|
||||
t2 = <Text>{" "}</Text>;
|
||||
t3 = <Text>{" * \u2588\u2588\u2588\u2588\u2588\u2593\u2593\u2591 "}</Text>;
|
||||
@@ -218,7 +218,7 @@ function AppleTerminalWelcomeV2(t0) {
|
||||
}
|
||||
let t2;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = <Text dimColor={true}>v{MACRO.VERSION} </Text>;
|
||||
t2 = <Text dimColor={true}>v{MACRO.DISPLAY_VERSION ?? MACRO.VERSION} </Text>;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
@@ -329,7 +329,7 @@ function AppleTerminalWelcomeV2(t0) {
|
||||
}
|
||||
let t2;
|
||||
if ($[24] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = <Text dimColor={true}>v{MACRO.VERSION} </Text>;
|
||||
t2 = <Text dimColor={true}>v{MACRO.DISPLAY_VERSION ?? MACRO.VERSION} </Text>;
|
||||
$[24] = t2;
|
||||
} else {
|
||||
t2 = $[24];
|
||||
|
||||
@@ -72,7 +72,7 @@ async function main(): Promise<void> {
|
||||
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
|
||||
// MACRO.VERSION is inlined at build time
|
||||
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
||||
console.log(`${MACRO.VERSION} (Claude Code)`);
|
||||
console.log(`${MACRO.DISPLAY_VERSION ?? MACRO.VERSION} (Open Claude)`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -322,10 +322,12 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
// No special flags detected, load and run the full CLI
|
||||
const {
|
||||
startCapturingEarlyInput
|
||||
} = await import('../utils/earlyInput.js');
|
||||
startCapturingEarlyInput();
|
||||
if (process.env.OPENCLAUDE_ENABLE_EARLY_INPUT === '1') {
|
||||
const {
|
||||
startCapturingEarlyInput
|
||||
} = await import('../utils/earlyInput.js');
|
||||
startCapturingEarlyInput();
|
||||
}
|
||||
profileCheckpoint('cli_before_main_import');
|
||||
const {
|
||||
main: cliMain
|
||||
|
||||
@@ -115,6 +115,7 @@ export default class App extends PureComponent<Props, State> {
|
||||
keyParseState = INITIAL_STATE;
|
||||
// Timer for flushing incomplete escape sequences
|
||||
incompleteEscapeTimer: NodeJS.Timeout | null = null;
|
||||
stdinMode: 'readable' | 'data' = process.env.OPENCLAUDE_USE_READABLE_STDIN === '1' ? 'readable' : 'data';
|
||||
// Timeout durations for incomplete sequences (ms)
|
||||
readonly NORMAL_TIMEOUT = 50; // Short timeout for regular esc sequences
|
||||
readonly PASTE_TIMEOUT = 500; // Longer timeout for paste operations
|
||||
@@ -228,7 +229,12 @@ export default class App extends PureComponent<Props, State> {
|
||||
stopCapturingEarlyInput();
|
||||
stdin.ref();
|
||||
stdin.setRawMode(true);
|
||||
stdin.addListener('readable', this.handleReadable);
|
||||
stdin.resume();
|
||||
if (this.stdinMode === 'data') {
|
||||
stdin.addListener('data', this.handleDataChunk);
|
||||
} else {
|
||||
stdin.addListener('readable', this.handleReadable);
|
||||
}
|
||||
// Enable bracketed paste mode
|
||||
this.props.stdout.write(EBP);
|
||||
// Enable terminal focus reporting (DECSET 1004)
|
||||
@@ -275,6 +281,8 @@ export default class App extends PureComponent<Props, State> {
|
||||
this.props.stdout.write(DBP);
|
||||
stdin.setRawMode(false);
|
||||
stdin.removeListener('readable', this.handleReadable);
|
||||
stdin.removeListener('data', this.handleDataChunk);
|
||||
stdin.pause();
|
||||
stdin.unref();
|
||||
}
|
||||
};
|
||||
@@ -366,6 +374,27 @@ export default class App extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
};
|
||||
handleDataChunk = (chunk: string | Buffer): void => {
|
||||
const now = Date.now();
|
||||
if (now - this.lastStdinTime > STDIN_RESUME_GAP_MS) {
|
||||
this.props.onStdinResume?.();
|
||||
}
|
||||
this.lastStdinTime = now;
|
||||
try {
|
||||
this.processInput(chunk);
|
||||
} catch (error) {
|
||||
logError(error);
|
||||
const {
|
||||
stdin
|
||||
} = this.props;
|
||||
if (this.rawModeEnabledCount > 0 && !stdin.listeners('data').includes(this.handleDataChunk)) {
|
||||
logForDebugging('handleDataChunk: re-attaching stdin data listener after error recovery', {
|
||||
level: 'warn'
|
||||
});
|
||||
stdin.addListener('data', this.handleDataChunk);
|
||||
}
|
||||
}
|
||||
};
|
||||
handleInput = (input: string | undefined): void => {
|
||||
// Exit on Ctrl+C
|
||||
if (input === '\x03' && this.props.exitOnCtrlC) {
|
||||
|
||||
@@ -165,6 +165,12 @@ const EXTENDED_KEYS_TERMINALS = [
|
||||
/** True if this terminal correctly handles extended key reporting
|
||||
* (Kitty keyboard protocol + xterm modifyOtherKeys). */
|
||||
export function supportsExtendedKeys(): boolean {
|
||||
// Open Claude defaults this off because some real terminals render the UI
|
||||
// but stop delivering normal typing once kitty/modifyOtherKeys negotiation
|
||||
// is enabled. Power users can opt back in explicitly.
|
||||
if (process.env.OPENCLAUDE_ENABLE_EXTENDED_KEYS !== '1') {
|
||||
return false
|
||||
}
|
||||
return EXTENDED_KEYS_TERMINALS.includes(env.terminal ?? '')
|
||||
}
|
||||
|
||||
|
||||
@@ -3782,7 +3782,7 @@ async function run(): Promise<CommanderCommand> {
|
||||
pendingHookMessages
|
||||
}, renderAndRun);
|
||||
}
|
||||
}).version(`${MACRO.VERSION} (Claude Code)`, '-v, --version', 'Output the version number');
|
||||
}).version(`${MACRO.DISPLAY_VERSION ?? MACRO.VERSION} (Open Claude)`, '-v, --version', 'Output the version number');
|
||||
|
||||
// Worktree flags
|
||||
program.option('-w, --worktree [name]', 'Create a new git worktree for this session (optionally specify a name)');
|
||||
|
||||
@@ -217,6 +217,7 @@ import { IdeOnboardingDialog } from '../components/IdeOnboardingDialog.js';
|
||||
import { EffortCallout, shouldShowEffortCallout } from '../components/EffortCallout.js';
|
||||
import type { EffortValue } from '../utils/effort.js';
|
||||
import { RemoteCallout } from '../components/RemoteCallout.js';
|
||||
import { getAPIProvider } from '../utils/model/providers.js';
|
||||
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
|
||||
const AntModelSwitchCallout = "external" === 'ant' ? require('../components/AntModelSwitchCallout.js').AntModelSwitchCallout : null;
|
||||
const shouldShowAntModelSwitch = "external" === 'ant' ? require('../components/AntModelSwitchCallout.js').shouldShowModelSwitchCallout : (): boolean => false;
|
||||
@@ -2675,7 +2676,7 @@ export function REPL({
|
||||
// useDeferredHookMessages) and attachment messages (appended by
|
||||
// processTextPrompt) — both pushed length past 1 on turn one, so the
|
||||
// title silently fell through to the "Claude Code" default.
|
||||
if (!titleDisabled && !sessionTitle && !agentTitle && !haikuTitleAttemptedRef.current) {
|
||||
if (getAPIProvider() === 'firstParty' && !titleDisabled && !sessionTitle && !agentTitle && !haikuTitleAttemptedRef.current) {
|
||||
const firstUserMessage = newMessages.find(m => m.type === 'user' && !m.isMeta);
|
||||
const text = firstUserMessage?.type === 'user' ? getContentText(firstUserMessage.message.content) : null;
|
||||
// Skip synthetic breadcrumbs — slash-command output, prompt-skill
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getModelBetas } from '../utils/betas.js'
|
||||
import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js'
|
||||
import { logError } from '../utils/log.js'
|
||||
import { getSmallFastModel } from '../utils/model/model.js'
|
||||
import { getAPIProvider } from '../utils/model/providers.js'
|
||||
import { isEssentialTrafficOnly } from '../utils/privacyLevel.js'
|
||||
import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from './analytics/index.js'
|
||||
import { logEvent } from './analytics/index.js'
|
||||
@@ -223,6 +224,10 @@ export async function checkQuotaStatus(): Promise<void> {
|
||||
return
|
||||
}
|
||||
|
||||
if (getAPIProvider() !== 'firstParty') {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if we should process rate limits (real subscriber or mock testing)
|
||||
if (!shouldProcessRateLimits(isClaudeAISubscriber())) {
|
||||
return
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getClaudeAIOAuthTokens } from 'src/utils/auth.js'
|
||||
import { getGlobalConfig, saveGlobalConfig } from 'src/utils/config.js'
|
||||
import { logForDebugging } from 'src/utils/debug.js'
|
||||
import { isEnvDefinedFalsy } from 'src/utils/envUtils.js'
|
||||
import { getAPIProvider } from 'src/utils/model/providers.js'
|
||||
import { clearMcpAuthCache } from './client.js'
|
||||
import { normalizeNameForMCP } from './normalization.js'
|
||||
import type { ScopedMcpServerConfig } from './types.js'
|
||||
@@ -39,6 +40,15 @@ const MCP_SERVERS_BETA_HEADER = 'mcp-servers-2025-12-04'
|
||||
export const fetchClaudeAIMcpConfigsIfEligible = memoize(
|
||||
async (): Promise<Record<string, ScopedMcpServerConfig>> => {
|
||||
try {
|
||||
if (getAPIProvider() !== 'firstParty') {
|
||||
logForDebugging('[claudeai-mcp] Skipped: non-first-party provider')
|
||||
logEvent('tengu_claudeai_mcp_eligibility', {
|
||||
state:
|
||||
'non_first_party_provider' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
})
|
||||
return {}
|
||||
}
|
||||
|
||||
if (isEnvDefinedFalsy(process.env.ENABLE_CLAUDEAI_MCP_SERVERS)) {
|
||||
logForDebugging('[claudeai-mcp] Disabled via env var')
|
||||
logEvent('tengu_claudeai_mcp_eligibility', {
|
||||
|
||||
@@ -109,7 +109,7 @@ export function execFileNoThrowWithCwd(
|
||||
// Use execa for cross-platform .bat/.cmd compatibility on Windows
|
||||
execa(file, args, {
|
||||
maxBuffer,
|
||||
signal: abortSignal,
|
||||
cancelSignal: abortSignal,
|
||||
timeout: finalTimeout,
|
||||
cwd: finalCwd,
|
||||
env: finalEnv,
|
||||
|
||||
@@ -36,6 +36,9 @@ import {
|
||||
import { createSignal } from './signal.js'
|
||||
|
||||
export function isFastModeEnabled(): boolean {
|
||||
if (getAPIProvider() !== 'firstParty') {
|
||||
return false
|
||||
}
|
||||
return !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_FAST_MODE)
|
||||
}
|
||||
|
||||
@@ -70,6 +73,10 @@ function getDisabledReasonMessage(
|
||||
}
|
||||
|
||||
export function getFastModeUnavailableReason(): string | null {
|
||||
if (getAPIProvider() !== 'firstParty') {
|
||||
return 'Fast mode is not available on third-party providers'
|
||||
}
|
||||
|
||||
if (!isFastModeEnabled()) {
|
||||
return 'Fast mode is not available'
|
||||
}
|
||||
@@ -109,13 +116,6 @@ export function getFastModeUnavailableReason(): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
// Only available for 1P (not Bedrock/Vertex/Foundry)
|
||||
if (getAPIProvider() !== 'firstParty') {
|
||||
const reason = 'Fast mode is not available on Bedrock, Vertex, or Foundry'
|
||||
logForDebugging(`Fast mode unavailable: ${reason}`)
|
||||
return reason
|
||||
}
|
||||
|
||||
if (orgStatus.status === 'disabled') {
|
||||
if (
|
||||
orgStatus.reason === 'network_error' ||
|
||||
|
||||
@@ -245,7 +245,7 @@ export function getLogoDisplayData(): {
|
||||
billingType: string
|
||||
agentName: string | undefined
|
||||
} {
|
||||
const version = process.env.DEMO_VERSION ?? MACRO.VERSION
|
||||
const version = process.env.DEMO_VERSION ?? MACRO.DISPLAY_VERSION ?? MACRO.VERSION
|
||||
const serverUrl = getDirectConnectServerUrl()
|
||||
const displayPath = process.env.DEMO_VERSION
|
||||
? '/code/claude'
|
||||
|
||||
Reference in New Issue
Block a user