更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)

* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files)

纯格式化:移除分号、React Compiler import、import 多行展开。
修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-2): 格式化 commands (79 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-5): 格式化 components其余 + hooks + tools (232 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md

- README.md: 大幅重写,更详细版本历史和配置示例
- Run.ps1: 新增 Windows 启动脚本
- TODO.md: 新增包完成清单
- V6.md: 删除(架构重构规划已不适用)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复以前的问题

* fix: 修复 login 面板的问题

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-04 23:24:27 +08:00
committed by GitHub
parent 02694918b5
commit 5b1a52b8e0
559 changed files with 103807 additions and 101817 deletions

View File

@@ -1,134 +1,152 @@
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
import { logEvent } from 'src/services/analytics/index.js';
import { logForDebugging } from 'src/utils/debug.js';
import { logError } from 'src/utils/log.js';
import { useInterval } from 'usehooks-ts';
import { useUpdateNotification } from '../hooks/useUpdateNotification.js';
import { Box, Text } from '../ink.js';
import type { AutoUpdaterResult } from '../utils/autoUpdater.js';
import { getMaxVersion, getMaxVersionMessage } from '../utils/autoUpdater.js';
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 * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import { logEvent } from 'src/services/analytics/index.js'
import { logForDebugging } from 'src/utils/debug.js'
import { logError } from 'src/utils/log.js'
import { useInterval } from 'usehooks-ts'
import { useUpdateNotification } from '../hooks/useUpdateNotification.js'
import { Box, Text } from '../ink.js'
import type { AutoUpdaterResult } from '../utils/autoUpdater.js'
import { getMaxVersion, getMaxVersionMessage } from '../utils/autoUpdater.js'
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'
/**
* Categorize error messages for analytics
*/
function getErrorType(errorMessage: string): string {
if (errorMessage.includes('timeout')) {
return 'timeout';
return 'timeout'
}
if (errorMessage.includes('Checksum mismatch')) {
return 'checksum_mismatch';
return 'checksum_mismatch'
}
if (errorMessage.includes('ENOENT') || errorMessage.includes('not found')) {
return 'not_found';
return 'not_found'
}
if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) {
return 'permission_denied';
return 'permission_denied'
}
if (errorMessage.includes('ENOSPC')) {
return 'disk_full';
return 'disk_full'
}
if (errorMessage.includes('npm')) {
return 'npm_error';
return 'npm_error'
}
if (errorMessage.includes('network') || errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND')) {
return 'network_error';
if (
errorMessage.includes('network') ||
errorMessage.includes('ECONNREFUSED') ||
errorMessage.includes('ENOTFOUND')
) {
return 'network_error'
}
return 'unknown';
return 'unknown'
}
type Props = {
isUpdating: boolean;
onChangeIsUpdating: (isUpdating: boolean) => void;
onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void;
autoUpdaterResult: AutoUpdaterResult | null;
showSuccessMessage: boolean;
verbose: boolean;
};
isUpdating: boolean
onChangeIsUpdating: (isUpdating: boolean) => void
onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void
autoUpdaterResult: AutoUpdaterResult | null
showSuccessMessage: boolean
verbose: boolean
}
export function NativeAutoUpdater({
isUpdating,
onChangeIsUpdating,
onAutoUpdaterResult,
autoUpdaterResult,
showSuccessMessage,
verbose
verbose,
}: Props): React.ReactNode {
const [versions, setVersions] = useState<{
current?: string | null;
latest?: string | null;
}>({});
const [maxVersionIssue, setMaxVersionIssue] = useState<string | null>(null);
const updateSemver = useUpdateNotification(autoUpdaterResult?.version);
const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest';
current?: string | null
latest?: string | null
}>({})
const [maxVersionIssue, setMaxVersionIssue] = useState<string | null>(null)
const updateSemver = useUpdateNotification(autoUpdaterResult?.version)
const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest'
// Track latest isUpdating value in a ref so the memoized checkForUpdates
// callback always sees the current value without changing callback identity
// (which would re-trigger the initial-check useEffect below and cause
// repeated downloads on remount — the upstream trigger for #22413).
const isUpdatingRef = useRef(isUpdating);
isUpdatingRef.current = isUpdating;
const isUpdatingRef = useRef(isUpdating)
isUpdatingRef.current = isUpdating
const checkForUpdates = React.useCallback(async () => {
if (isUpdatingRef.current) {
return;
return
}
if (("production" as string) === 'test' || ("production" as string) === 'development') {
logForDebugging('NativeAutoUpdater: Skipping update check in test/dev environment');
return;
if (
"production" === 'test' ||
"production" === 'development'
) {
logForDebugging(
'NativeAutoUpdater: Skipping update check in test/dev environment',
)
return
}
if (isAutoUpdaterDisabled()) {
return;
return
}
onChangeIsUpdating(true);
const startTime = Date.now();
onChangeIsUpdating(true)
const startTime = Date.now()
// Log the start of an auto-update check for funnel analysis
logEvent('tengu_native_auto_updater_start', {});
logEvent('tengu_native_auto_updater_start', {})
try {
// Check if current version is above the max allowed version
const maxVersion = await getMaxVersion();
const maxVersion = await getMaxVersion()
if (maxVersion && gt(MACRO.VERSION, maxVersion)) {
const msg = await getMaxVersionMessage();
setMaxVersionIssue(msg ?? 'affects your version');
const msg = await getMaxVersionMessage()
setMaxVersionIssue(msg ?? 'affects your version')
}
const result = await installLatest(channel);
const currentVersion = MACRO.VERSION;
const latencyMs = Date.now() - startTime;
const result = await installLatest(channel)
const currentVersion = MACRO.VERSION
const latencyMs = Date.now() - startTime
// Handle lock contention gracefully - just return without treating as error
if (result.lockFailed) {
logEvent('tengu_native_auto_updater_lock_contention', {
latency_ms: latencyMs
});
return; // Silently skip this update check, will try again later
latency_ms: latencyMs,
})
return // Silently skip this update check, will try again later
}
// Update versions for display
setVersions({
current: currentVersion,
latest: result.latestVersion
});
setVersions({ current: currentVersion, latest: result.latestVersion })
if (result.wasUpdated) {
logEvent('tengu_native_auto_updater_success', {
latency_ms: latencyMs
});
latency_ms: latencyMs,
})
onAutoUpdaterResult({
version: result.latestVersion,
status: 'success'
});
status: 'success',
})
} else {
// Already up to date
logEvent('tengu_native_auto_updater_up_to_date', {
latency_ms: latencyMs
});
latency_ms: latencyMs,
})
}
} catch (error) {
const latencyMs = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
logError(error);
const errorType = getErrorType(errorMessage);
const latencyMs = Date.now() - startTime
const errorMessage =
error instanceof Error ? error.message : String(error)
logError(error)
const errorType = getErrorType(errorMessage)
logEvent('tengu_native_auto_updater_fail', {
latency_ms: latencyMs,
error_timeout: errorType === 'timeout',
@@ -137,56 +155,77 @@ export function NativeAutoUpdater({
error_permission: errorType === 'permission_denied',
error_disk_full: errorType === 'disk_full',
error_npm: errorType === 'npm_error',
error_network: errorType === 'network_error'
});
error_network: errorType === 'network_error',
})
onAutoUpdaterResult({
version: null,
status: 'install_failed'
});
status: 'install_failed',
})
} finally {
onChangeIsUpdating(false);
onChangeIsUpdating(false)
}
// isUpdating intentionally omitted from deps; we read isUpdatingRef
// instead so the guard is always current without changing callback
// identity (which would re-trigger the initial-check useEffect below).
// eslint-disable-next-line react-hooks/exhaustive-deps
// biome-ignore lint/correctness/useExhaustiveDependencies: isUpdating read via ref
}, [onAutoUpdaterResult, channel]);
}, [onAutoUpdaterResult, channel])
// Initial check
useEffect(() => {
void checkForUpdates();
}, [checkForUpdates]);
void checkForUpdates()
}, [checkForUpdates])
// Check every 30 minutes
useInterval(checkForUpdates, 30 * 60 * 1000);
const hasUpdateResult = !!autoUpdaterResult?.version;
const hasVersionInfo = !!versions.current && !!versions.latest;
useInterval(checkForUpdates, 30 * 60 * 1000)
const hasUpdateResult = !!autoUpdaterResult?.version
const hasVersionInfo = !!versions.current && !!versions.latest
// Show the component when:
// - warning banner needed (above max version), or
// - there's an update result to display (success/error), or
// - actively checking and we have version info to show
const shouldRender = !!maxVersionIssue || hasUpdateResult || isUpdating && hasVersionInfo;
const shouldRender =
!!maxVersionIssue || hasUpdateResult || (isUpdating && hasVersionInfo)
if (!shouldRender) {
return null;
return null
}
return <Box flexDirection="row" gap={1}>
{verbose && <Text dimColor wrap="truncate">
return (
<Box flexDirection="row" gap={1}>
{verbose && (
<Text dimColor wrap="truncate">
current: {versions.current} &middot; {channel}: {versions.latest}
</Text>}
{isUpdating ? <Box>
</Text>
)}
{isUpdating ? (
<Box>
<Text dimColor wrap="truncate">
Checking for updates
</Text>
</Box> : autoUpdaterResult?.status === 'success' && showSuccessMessage && updateSemver && <Text color="success" wrap="truncate">
</Box>
) : (
autoUpdaterResult?.status === 'success' &&
showSuccessMessage &&
updateSemver && (
<Text color="success" wrap="truncate">
Update installed · Restart to update
</Text>}
{autoUpdaterResult?.status === 'install_failed' && <Text color="error" wrap="truncate">
</Text>
)
)}
{autoUpdaterResult?.status === 'install_failed' && (
<Text color="error" wrap="truncate">
Auto-update failed &middot; Try <Text bold>/status</Text>
</Text>}
{maxVersionIssue && (process.env.USER_TYPE) === 'ant' && <Text color="warning">
</Text>
)}
{maxVersionIssue && process.env.USER_TYPE === 'ant' && (
<Text color="warning">
Known issue: {maxVersionIssue} &middot; Run{' '}
<Text bold>claude rollback --safe</Text> to downgrade
</Text>}
</Box>;
</Text>
)}
</Box>
)
}