axios+telemetry cleanup

This commit is contained in:
2026-04-02 15:19:11 +03:00
parent a3cbca1e11
commit 7e1eac8002
100 changed files with 3048 additions and 4491 deletions

View File

@@ -12,6 +12,7 @@ import { installOrUpdateClaudePackage, localInstallationExists } from '../utils/
import { removeInstalledSymlink } from '../utils/nativeInstaller/index.js';
import { gt, gte } from '../utils/semver.js';
import { getInitialSettings } from '../utils/settings/settings.js';
import { VERSION, PACKAGE_URL } from '../constants/product.js';
type Props = {
isUpdating: boolean;
onChangeIsUpdating: (isUpdating: boolean) => void;
@@ -53,7 +54,7 @@ export function AutoUpdater({
logForDebugging('AutoUpdater: Skipping update check in test/dev environment');
return;
}
const currentVersion = MACRO.VERSION;
const currentVersion = VERSION;
const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest';
let latestVersion = await getLatestVersion(channel);
const isDisabled = isAutoUpdaterDisabled();
@@ -190,7 +191,7 @@ export function AutoUpdater({
{(autoUpdaterResult?.status === 'install_failed' || autoUpdaterResult?.status === 'no_permissions') && <Text color="error" wrap="truncate">
Auto-update failed &middot; Try <Text bold>claude doctor</Text> or{' '}
<Text bold>
{hasLocalInstall ? `cd ~/.claude/local && npm update ${MACRO.PACKAGE_URL}` : `npm i -g ${MACRO.PACKAGE_URL}`}
{hasLocalInstall ? `cd ~/.claude/local && npm update ${PACKAGE_URL}` : `npm i -g ${PACKAGE_URL}`}
</Text>
</Text>}
</Box>;

View File

@@ -1,4 +1,4 @@
import axios from 'axios';
import { isHttpError, nativeRequest } from '../utils/http.js';
import { readFile, stat } from 'fs/promises';
import * as React from 'react';
import { useCallback, useEffect, useState } from 'react';
@@ -28,6 +28,7 @@ import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js';
import { Byline } from './design-system/Byline.js';
import { Dialog } from './design-system/Dialog.js';
import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js';
import { VERSION } from 'src/constants/product.js';
import TextInput from './TextInput.js';
// This value was determined experimentally by testing the URL length limit
@@ -211,7 +212,7 @@ export function Feedback({
platform: env.platform,
gitRepo: envInfo.isGit,
terminal: env.terminal,
version: MACRO.VERSION,
version: VERSION,
transcript: normalizeMessagesForAPI(messages),
errors: sanitizedErrors,
lastApiRequest: getLastAPIRequest(),
@@ -343,7 +344,7 @@ export function Feedback({
<Text>
- Environment info:{' '}
<Text dimColor>
{env.platform}, {env.terminal}, v{MACRO.VERSION}
{env.platform}, {env.terminal}, v{VERSION}
</Text>
</Text>
{envInfo.gitState && <Text>
@@ -396,7 +397,7 @@ export function createGitHubIssueUrl(feedbackId: string, title: string, descript
}>): string {
const sanitizedTitle = redactSensitiveInfo(title);
const sanitizedDescription = redactSensitiveInfo(description);
const bodyPrefix = `**Bug Description**\n${sanitizedDescription}\n\n` + `**Environment Info**\n` + `- Platform: ${env.platform}\n` + `- Terminal: ${env.terminal}\n` + `- Version: ${MACRO.VERSION || 'unknown'}\n` + `- Feedback ID: ${feedbackId}\n` + `\n**Errors**\n\`\`\`json\n`;
const bodyPrefix = `**Bug Description**\n${sanitizedDescription}\n\n` + `**Environment Info**\n` + `- Platform: ${env.platform}\n` + `- Terminal: ${env.terminal}\n` + `- Version: ${VERSION || 'unknown'}\n` + `- Feedback ID: ${feedbackId}\n` + `\n**Errors**\n\`\`\`json\n`;
const errorSuffix = `\n\`\`\`\n`;
const errorsJson = jsonStringify(errors);
const baseUrl = `${GITHUB_ISSUES_REPO_URL}/new?title=${encodeURIComponent(sanitizedTitle)}&labels=user-reported,bug&body=`;
@@ -540,12 +541,13 @@ async function submitFeedback(data: FeedbackData, signal?: AbortSignal): Promise
'User-Agent': getUserAgent(),
...authResult.headers
};
const response = await axios.post('https://api.anthropic.com/api/claude_cli_feedback', {
content: jsonStringify(data)
}, {
const response = await nativeRequest('https://api.anthropic.com/api/claude_cli_feedback', {
method: 'POST',
headers,
body: {
content: jsonStringify(data)
},
timeout: 30000,
// 30 second timeout to prevent hanging
signal
});
if (response.status === 200) {
@@ -566,14 +568,13 @@ async function submitFeedback(data: FeedbackData, signal?: AbortSignal): Promise
success: false
};
} catch (err) {
// Handle cancellation/abort - don't log as error
if (axios.isCancel(err)) {
if (err instanceof Error && err.name === 'AbortError') {
return {
success: false
};
}
if (axios.isAxiosError(err) && err.response?.status === 403) {
const errorData = err.response.data;
if (isHttpError(err) && err.status === 403) {
const errorData = err.data;
if (errorData?.error?.type === 'permission_error' && errorData?.error?.message?.includes('Custom data retention settings')) {
sanitizeAndLogError(new Error('Cannot submit feedback because custom data retention settings are enabled'));
return {

View File

@@ -1,21 +1,12 @@
import axios from 'axios'
import { readFile, stat } from 'fs/promises'
import type { Message } from '../../types/message.js'
import { checkAndRefreshOAuthTokenIfNeeded } from '../../utils/auth.js'
import { logForDebugging } from '../../utils/debug.js'
import { errorMessage } from '../../utils/errors.js'
import { getAuthHeaders, getUserAgent } from '../../utils/http.js'
import { normalizeMessagesForAPI } from '../../utils/messages.js'
import {
extractAgentIdsFromMessages,
getTranscriptPath,
loadSubagentTranscripts,
MAX_TRANSCRIPT_READ_BYTES,
} from '../../utils/sessionStorage.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { redactSensitiveInfo } from '../Feedback.js'
/**
* Transcript Share Service - Stubbed
*
* This service is stubbed to ensure no session transcripts or user
* identification data is sent to external services, even during
* feedback surveys.
*/
type TranscriptShareResult = {
export type TranscriptShareResult = {
success: boolean
transcriptId?: string
}
@@ -27,86 +18,11 @@ export type TranscriptShareTrigger =
| 'memory_survey'
export async function submitTranscriptShare(
messages: Message[],
trigger: TranscriptShareTrigger,
appearanceId: string,
_messages: any[],
_trigger: TranscriptShareTrigger,
_appearanceId: string,
): Promise<TranscriptShareResult> {
try {
logForDebugging('Collecting transcript for sharing', { level: 'info' })
const transcript = normalizeMessagesForAPI(messages)
// Collect subagent transcripts
const agentIds = extractAgentIdsFromMessages(messages)
const subagentTranscripts = await loadSubagentTranscripts(agentIds)
// Read raw JSONL transcript (with size guard to prevent OOM)
let rawTranscriptJsonl: string | undefined
try {
const transcriptPath = getTranscriptPath()
const { size } = await stat(transcriptPath)
if (size <= MAX_TRANSCRIPT_READ_BYTES) {
rawTranscriptJsonl = await readFile(transcriptPath, 'utf-8')
} else {
logForDebugging(
`Skipping raw transcript read: file too large (${size} bytes)`,
{ level: 'warn' },
)
}
} catch {
// File may not exist
}
const data = {
trigger,
version: MACRO.VERSION,
platform: process.platform,
transcript,
subagentTranscripts:
Object.keys(subagentTranscripts).length > 0
? subagentTranscripts
: undefined,
rawTranscriptJsonl,
}
const content = redactSensitiveInfo(jsonStringify(data))
await checkAndRefreshOAuthTokenIfNeeded()
const authResult = getAuthHeaders()
if (authResult.error) {
return { success: false }
}
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'User-Agent': getUserAgent(),
...authResult.headers,
}
const response = await axios.post(
'https://api.anthropic.com/api/claude_code_shared_session_transcripts',
{ content, appearance_id: appearanceId },
{
headers,
timeout: 30000,
},
)
if (response.status === 200 || response.status === 201) {
const result = response.data
logForDebugging('Transcript shared successfully', { level: 'info' })
return {
success: true,
transcriptId: result?.transcript_id,
}
}
return { success: false }
} catch (err) {
logForDebugging(errorMessage(err), {
level: 'error',
})
return { success: false }
}
// Always return failure to prevent sharing data.
// This effectively disables the feature without crashing the UI.
return { success: false };
}

View File

@@ -26,6 +26,7 @@ import { EmergencyTip } from './EmergencyTip.js';
import { VoiceModeNotice } from './VoiceModeNotice.js';
import { Opus1mMergeNotice } from './Opus1mMergeNotice.js';
import { feature } from 'bun:bundle';
import { VERSION } from '../../constants/product.js';
// Conditional require so ChannelsNotice.tsx tree-shakes when both flags are
// false. A module-scope helper component inside a feature() ternary does NOT
@@ -92,7 +93,7 @@ export function LogoV2() {
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t2 = () => {
const currentConfig = getGlobalConfig();
if (currentConfig.lastReleaseNotesSeen === MACRO.VERSION) {
if (currentConfig.lastReleaseNotesSeen === VERSION) {
return;
}
saveGlobalConfig(_temp3);
@@ -526,12 +527,12 @@ export function LogoV2() {
return t41;
}
function _temp3(current) {
if (current.lastReleaseNotesSeen === MACRO.VERSION) {
if (current.lastReleaseNotesSeen === VERSION) {
return current;
}
return {
...current,
lastReleaseNotesSeen: MACRO.VERSION
lastReleaseNotesSeen: VERSION
};
}
function _temp2(s_0) {

View File

@@ -2,6 +2,7 @@ import { c as _c } from "react/compiler-runtime";
import React from 'react';
import { Box, Text, useTheme } from 'src/ink.js';
import { env } from '../../utils/env.js';
import { VERSION } from '../../constants/product.js';
const WELCOME_V2_WIDTH = 58;
export function WelcomeV2() {
const $ = _c(35);
@@ -28,7 +29,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 Claude Code"} </Text><Text dimColor={true}>v{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 +114,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 Claude Code"} </Text><Text dimColor={true}>v{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 +219,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{VERSION} </Text>;
$[2] = t2;
} else {
t2 = $[2];
@@ -329,7 +330,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{VERSION} </Text>;
$[24] = t2;
} else {
t2 = $[24];

View File

@@ -12,6 +12,7 @@ import { isAutoUpdaterDisabled } from '../utils/config.js';
import { installLatest } from '../utils/nativeInstaller/index.js';
import { gt } from '../utils/semver.js';
import { getInitialSettings } from '../utils/settings/settings.js';
import { VERSION } from '../constants/product.js';
/**
* Categorize error messages for analytics
@@ -89,12 +90,12 @@ export function NativeAutoUpdater({
try {
// Check if current version is above the max allowed version
const maxVersion = await getMaxVersion();
if (maxVersion && gt(MACRO.VERSION, maxVersion)) {
if (maxVersion && gt(VERSION, maxVersion)) {
const msg = await getMaxVersionMessage();
setMaxVersionIssue(msg ?? 'affects your version');
}
const result = await installLatest(channel);
const currentVersion = MACRO.VERSION;
const currentVersion = VERSION;
const latencyMs = Date.now() - startTime;
// Handle lock contention gracefully - just return without treating as error