mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-24 02:29:13 +00:00
Compare commits
19 Commits
install-sh
...
i18n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d58bb0424e | ||
|
|
01c0887a14 | ||
|
|
de56af8cee | ||
|
|
0ef11dff91 | ||
|
|
d5c092ea92 | ||
|
|
a85ec013f9 | ||
|
|
450c09b4b8 | ||
|
|
406e6b5e1f | ||
|
|
32982a16e6 | ||
|
|
12bebe1b5f | ||
|
|
78ea9d3fde | ||
|
|
805a919f69 | ||
|
|
47083e810a | ||
|
|
1dce30b39f | ||
|
|
0707cb5ddf | ||
|
|
a33a00256d | ||
|
|
6cccf9cc59 | ||
|
|
88b5717f83 | ||
|
|
da2be8045f |
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -224,4 +224,5 @@ jobs:
|
||||
run: |-
|
||||
gh issue create \
|
||||
--title "Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')" \
|
||||
--body "The release workflow failed. See the full run for details: ${DETAILS_URL}"
|
||||
--body "The release workflow failed. See the full run for details: ${DETAILS_URL}" \
|
||||
--label "kind/bug,release-failure"
|
||||
|
||||
@@ -54,7 +54,6 @@ describe('JSON output', () => {
|
||||
});
|
||||
|
||||
it('should return a JSON error for enforced auth mismatch before running', async () => {
|
||||
const originalOpenaiApiKey = process.env['OPENAI_API_KEY'];
|
||||
process.env['OPENAI_API_KEY'] = 'test-key';
|
||||
await rig.setup('json-output-auth-mismatch', {
|
||||
settings: {
|
||||
@@ -69,7 +68,7 @@ describe('JSON output', () => {
|
||||
} catch (e) {
|
||||
thrown = e as Error;
|
||||
} finally {
|
||||
process.env['OPENAI_API_KEY'] = originalOpenaiApiKey;
|
||||
delete process.env['OPENAI_API_KEY'];
|
||||
}
|
||||
|
||||
expect(thrown).toBeDefined();
|
||||
|
||||
@@ -1951,7 +1951,7 @@ describe('InputPrompt', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it.skip('expands and collapses long suggestion via Right/Left arrows', async () => {
|
||||
it('expands and collapses long suggestion via Right/Left arrows', async () => {
|
||||
props.shellModeActive = false;
|
||||
const longValue = 'l'.repeat(200);
|
||||
|
||||
|
||||
@@ -99,13 +99,13 @@ export const AgentExecutionDisplay: React.FC<AgentExecutionDisplayProps> = ({
|
||||
data.toolCalls && data.toolCalls.length > MAX_TOOL_CALLS;
|
||||
|
||||
if (hasMoreToolCalls || hasMoreLines) {
|
||||
return 'Press ctrl+e to show less, ctrl+f to show more.';
|
||||
return 'Press ctrl+r to show less, ctrl+e to show more.';
|
||||
}
|
||||
return 'Press ctrl+e to show less.';
|
||||
return 'Press ctrl+r to show less.';
|
||||
}
|
||||
|
||||
if (displayMode === 'verbose') {
|
||||
return 'Press ctrl+f to show less.';
|
||||
return 'Press ctrl+e to show less.';
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -114,13 +114,13 @@ export const AgentExecutionDisplay: React.FC<AgentExecutionDisplayProps> = ({
|
||||
// Handle keyboard shortcuts to control display mode
|
||||
useKeypress(
|
||||
(key) => {
|
||||
if (key.ctrl && key.name === 'e') {
|
||||
// ctrl+e toggles between compact and default
|
||||
if (key.ctrl && key.name === 'r') {
|
||||
// ctrl+r toggles between compact and default
|
||||
setDisplayMode((current) =>
|
||||
current === 'compact' ? 'default' : 'compact',
|
||||
);
|
||||
} else if (key.ctrl && key.name === 'f') {
|
||||
// ctrl+f toggles between default and verbose
|
||||
} else if (key.ctrl && key.name === 'e') {
|
||||
// ctrl+e toggles between default and verbose
|
||||
setDisplayMode((current) =>
|
||||
current === 'default' ? 'verbose' : 'default',
|
||||
);
|
||||
@@ -157,7 +157,7 @@ export const AgentExecutionDisplay: React.FC<AgentExecutionDisplayProps> = ({
|
||||
{data.toolCalls.length > 1 && !data.pendingConfirmation && (
|
||||
<Box flexDirection="row" paddingLeft={4}>
|
||||
<Text color={theme.text.secondary}>
|
||||
+{data.toolCalls.length - 1} more tool calls (ctrl+e to
|
||||
+{data.toolCalls.length - 1} more tool calls (ctrl+r to
|
||||
expand)
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -848,7 +848,7 @@ export async function start_sandbox(
|
||||
sandboxProcess?.on('close', (code, signal) => {
|
||||
process.stdin.resume();
|
||||
if (code !== 0 && code !== null) {
|
||||
console.error(
|
||||
console.log(
|
||||
`Sandbox process exited with code: ${code}, signal: ${signal}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -286,9 +286,9 @@ describe('QwenLogger', () => {
|
||||
event_type: 'action',
|
||||
type: 'ide',
|
||||
name: 'ide_connection',
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
connection_type: IdeConnectionType.SESSION,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -307,10 +307,8 @@ describe('QwenLogger', () => {
|
||||
type: 'overflow',
|
||||
name: 'kitty_sequence_overflow',
|
||||
subtype: 'kitty_sequence_overflow',
|
||||
properties: {
|
||||
sequence_length: 1024,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
sequence_length: 1024,
|
||||
truncated_sequence: 'truncated...',
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -259,7 +259,7 @@ export class QwenLogger {
|
||||
: '',
|
||||
},
|
||||
_v: `qwen-code@${version}`,
|
||||
} as RumPayload;
|
||||
};
|
||||
}
|
||||
|
||||
flushIfNeeded(): void {
|
||||
@@ -368,10 +368,12 @@ export class QwenLogger {
|
||||
const applicationEvent = this.createViewEvent('session', 'session_start', {
|
||||
properties: {
|
||||
model: event.model,
|
||||
approval_mode: event.approval_mode,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
embedding_model: event.embedding_model,
|
||||
sandbox_enabled: event.sandbox_enabled,
|
||||
core_tools_enabled: event.core_tools_enabled,
|
||||
approval_mode: event.approval_mode,
|
||||
api_key_enabled: event.api_key_enabled,
|
||||
vertex_ai_enabled: event.vertex_ai_enabled,
|
||||
debug_enabled: event.debug_enabled,
|
||||
@@ -379,7 +381,7 @@ export class QwenLogger {
|
||||
telemetry_enabled: event.telemetry_enabled,
|
||||
telemetry_log_user_prompts_enabled:
|
||||
event.telemetry_log_user_prompts_enabled,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Flush start event immediately
|
||||
@@ -408,10 +410,10 @@ export class QwenLogger {
|
||||
'conversation',
|
||||
'conversation_finished',
|
||||
{
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
approval_mode: event.approvalMode,
|
||||
turn_count: event.turnCount,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -425,8 +427,10 @@ export class QwenLogger {
|
||||
properties: {
|
||||
auth_type: event.auth_type,
|
||||
prompt_id: event.prompt_id,
|
||||
prompt_length: event.prompt_length,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
prompt_length: event.prompt_length,
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -435,10 +439,10 @@ export class QwenLogger {
|
||||
|
||||
logSlashCommandEvent(event: SlashCommandEvent): void {
|
||||
const rumEvent = this.createActionEvent('user', 'slash_command', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
command: event.command,
|
||||
subcommand: event.subcommand,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -447,9 +451,9 @@ export class QwenLogger {
|
||||
|
||||
logModelSlashCommandEvent(event: ModelSlashCommandEvent): void {
|
||||
const rumEvent = this.createActionEvent('user', 'model_slash_command', {
|
||||
properties: {
|
||||
model: event.model_name,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
model_name: event.model_name,
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -465,13 +469,15 @@ export class QwenLogger {
|
||||
properties: {
|
||||
prompt_id: event.prompt_id,
|
||||
response_id: event.response_id,
|
||||
tool_name: event.function_name,
|
||||
permission: event.decision,
|
||||
success: event.success ? 1 : 0,
|
||||
duration_ms: event.duration_ms,
|
||||
error_type: event.error_type,
|
||||
error_message: event.error,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
function_name: event.function_name,
|
||||
decision: event.decision,
|
||||
success: event.success,
|
||||
duration_ms: event.duration_ms,
|
||||
error: event.error,
|
||||
error_type: event.error_type,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -484,14 +490,14 @@ export class QwenLogger {
|
||||
'tool',
|
||||
`file_operation#${event.tool_name}`,
|
||||
{
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
tool_name: event.tool_name,
|
||||
operation: event.operation,
|
||||
lines: event.lines,
|
||||
mimetype: event.mimetype,
|
||||
extension: event.extension,
|
||||
programming_language: event.programming_language,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -501,15 +507,11 @@ export class QwenLogger {
|
||||
|
||||
logSubagentExecutionEvent(event: SubagentExecutionEvent): void {
|
||||
const rumEvent = this.createActionEvent('tool', 'subagent_execution', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
subagent_name: event.subagent_name,
|
||||
status: event.status,
|
||||
terminate_reason: event.terminate_reason,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
...(event.execution_summary
|
||||
? { execution_summary: event.execution_summary }
|
||||
: {}),
|
||||
execution_summary: event.execution_summary,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -519,10 +521,8 @@ export class QwenLogger {
|
||||
|
||||
logToolOutputTruncatedEvent(event: ToolOutputTruncatedEvent): void {
|
||||
const rumEvent = this.createActionEvent('tool', 'tool_output_truncated', {
|
||||
properties: {
|
||||
tool_name: event.tool_name,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
tool_name: event.tool_name,
|
||||
original_content_length: event.original_content_length,
|
||||
truncated_content_length: event.truncated_content_length,
|
||||
threshold: event.threshold,
|
||||
@@ -595,8 +595,10 @@ export class QwenLogger {
|
||||
auth_type: event.auth_type,
|
||||
model: event.model,
|
||||
prompt_id: event.prompt_id,
|
||||
error_type: event.error_type,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
error_type: event.error_type,
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -621,11 +623,11 @@ export class QwenLogger {
|
||||
{
|
||||
subtype: 'content_retry_failure',
|
||||
message: `Content retry failed after ${event.total_attempts} attempts`,
|
||||
properties: {
|
||||
error_type: event.final_error_type,
|
||||
snapshots: JSON.stringify({
|
||||
total_attempts: event.total_attempts,
|
||||
final_error_type: event.final_error_type,
|
||||
total_duration_ms: event.total_duration_ms,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -654,8 +656,10 @@ export class QwenLogger {
|
||||
subtype: 'loop_detected',
|
||||
properties: {
|
||||
prompt_id: event.prompt_id,
|
||||
error_type: event.loop_type,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
loop_type: event.loop_type,
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -668,10 +672,8 @@ export class QwenLogger {
|
||||
'kitty_sequence_overflow',
|
||||
{
|
||||
subtype: 'kitty_sequence_overflow',
|
||||
properties: {
|
||||
sequence_length: event.sequence_length,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
sequence_length: event.sequence_length,
|
||||
truncated_sequence: event.truncated_sequence,
|
||||
}),
|
||||
},
|
||||
@@ -684,9 +686,7 @@ export class QwenLogger {
|
||||
// ide events
|
||||
logIdeConnectionEvent(event: IdeConnectionEvent): void {
|
||||
const rumEvent = this.createActionEvent('ide', 'ide_connection', {
|
||||
properties: {
|
||||
connection_type: event.connection_type,
|
||||
},
|
||||
snapshots: JSON.stringify({ connection_type: event.connection_type }),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -696,12 +696,12 @@ export class QwenLogger {
|
||||
// extension events
|
||||
logExtensionInstallEvent(event: ExtensionInstallEvent): void {
|
||||
const rumEvent = this.createActionEvent('extension', 'extension_install', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
extension_name: event.extension_name,
|
||||
extension_version: event.extension_version,
|
||||
extension_source: event.extension_source,
|
||||
status: event.status,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -713,10 +713,10 @@ export class QwenLogger {
|
||||
'extension',
|
||||
'extension_uninstall',
|
||||
{
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
extension_name: event.extension_name,
|
||||
status: event.status,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -726,10 +726,10 @@ export class QwenLogger {
|
||||
|
||||
logExtensionEnableEvent(event: ExtensionEnableEvent): void {
|
||||
const rumEvent = this.createActionEvent('extension', 'extension_enable', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
extension_name: event.extension_name,
|
||||
setting_scope: event.setting_scope,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -738,10 +738,10 @@ export class QwenLogger {
|
||||
|
||||
logExtensionDisableEvent(event: ExtensionDisableEvent): void {
|
||||
const rumEvent = this.createActionEvent('extension', 'extension_disable', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
extension_name: event.extension_name,
|
||||
setting_scope: event.setting_scope,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -749,15 +749,18 @@ export class QwenLogger {
|
||||
}
|
||||
|
||||
logAuthEvent(event: AuthEvent): void {
|
||||
const snapshots: Record<string, unknown> = {
|
||||
auth_type: event.auth_type,
|
||||
action_type: event.action_type,
|
||||
status: event.status,
|
||||
};
|
||||
|
||||
if (event.error_message) {
|
||||
snapshots['error_message'] = event.error_message;
|
||||
}
|
||||
|
||||
const rumEvent = this.createActionEvent('auth', 'auth', {
|
||||
properties: {
|
||||
auth_type: event.auth_type,
|
||||
action_type: event.action_type,
|
||||
success: event.status === 'success' ? 1 : 0,
|
||||
error_type: event.status !== 'success' ? event.status : undefined,
|
||||
error_message:
|
||||
event.status === 'error' ? event.error_message : undefined,
|
||||
},
|
||||
snapshots: JSON.stringify(snapshots),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -778,13 +781,13 @@ export class QwenLogger {
|
||||
|
||||
logRipgrepFallbackEvent(event: RipgrepFallbackEvent): void {
|
||||
const rumEvent = this.createActionEvent('misc', 'ripgrep_fallback', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
use_ripgrep: event.use_ripgrep,
|
||||
use_builtin_ripgrep: event.use_builtin_ripgrep,
|
||||
error_message: event.error,
|
||||
},
|
||||
error: event.error ?? undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -806,9 +809,11 @@ export class QwenLogger {
|
||||
const rumEvent = this.createActionEvent('misc', 'next_speaker_check', {
|
||||
properties: {
|
||||
prompt_id: event.prompt_id,
|
||||
},
|
||||
snapshots: JSON.stringify({
|
||||
finish_reason: event.finish_reason,
|
||||
result: event.result,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -817,10 +822,10 @@ export class QwenLogger {
|
||||
|
||||
logChatCompressionEvent(event: ChatCompressionEvent): void {
|
||||
const rumEvent = this.createActionEvent('misc', 'chat_compression', {
|
||||
properties: {
|
||||
snapshots: JSON.stringify({
|
||||
tokens_before: event.tokens_before,
|
||||
tokens_after: event.tokens_after,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
@@ -829,11 +834,11 @@ export class QwenLogger {
|
||||
|
||||
logContentRetryEvent(event: ContentRetryEvent): void {
|
||||
const rumEvent = this.createActionEvent('misc', 'content_retry', {
|
||||
properties: {
|
||||
error_type: event.error_type,
|
||||
snapshots: JSON.stringify({
|
||||
attempt_number: event.attempt_number,
|
||||
error_type: event.error_type,
|
||||
retry_delay_ms: event.retry_delay_ms,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.enqueueLogEvent(rumEvent);
|
||||
|
||||
@@ -1,623 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Define color output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Define log functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# Check if command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check if it's a development machine environment
|
||||
is_dev_machine() {
|
||||
# Check if development machine specific directories or files exist
|
||||
if [ -d "/apsara" ] || [ -d "/home/admin" ] || [ -f "/etc/redhat-release" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if running in WSL
|
||||
is_wsl() {
|
||||
if [ -f /proc/version ] && grep -qi microsoft /proc/version; then
|
||||
return 0
|
||||
fi
|
||||
if [ -n "$WSL_DISTRO_NAME" ] || [ -n "$WSL_INTEROP" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Get Windows npm prefix (for Git Bash/Cygwin/MSYS)
|
||||
get_windows_npm_prefix() {
|
||||
# Try to get npm prefix
|
||||
local npm_prefix=$(npm config get prefix 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$npm_prefix" ]; then
|
||||
echo "$npm_prefix"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try common Windows npm locations
|
||||
if [ -n "$APPDATA" ]; then
|
||||
echo "$APPDATA/npm"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try Program Files
|
||||
if [ -d "/c/Program Files/nodejs" ]; then
|
||||
echo "/c/Program Files/nodejs"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback
|
||||
echo "$HOME/.npm-global"
|
||||
}
|
||||
|
||||
# Get shell configuration file
|
||||
get_shell_profile() {
|
||||
local current_shell=$(basename "$SHELL")
|
||||
case "$current_shell" in
|
||||
bash)
|
||||
echo "$HOME/.bashrc"
|
||||
;;
|
||||
zsh)
|
||||
echo "$HOME/.zshrc"
|
||||
;;
|
||||
fish)
|
||||
echo "$HOME/.config/fish/config.fish"
|
||||
;;
|
||||
*)
|
||||
echo "$HOME/.profile"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Clean npm configuration conflicts
|
||||
clean_npmrc_conflict() {
|
||||
local npmrc="$HOME/.npmrc"
|
||||
if [[ -f "$npmrc" ]]; then
|
||||
log_info "Cleaning npmrc conflicts..."
|
||||
grep -Ev '^(prefix|globalconfig) *= *' "$npmrc" > "${npmrc}.tmp" && mv -f "${npmrc}.tmp" "$npmrc" || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Install nvm
|
||||
install_nvm() {
|
||||
local NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
local NVM_VERSION="${NVM_VERSION:-v0.40.3}"
|
||||
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
log_info "nvm is already installed at $NVM_DIR"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Installing nvm ${NVM_VERSION}..."
|
||||
|
||||
# Install nvm using official installer
|
||||
if curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash; then
|
||||
log_success "nvm installed successfully"
|
||||
|
||||
# Load nvm for current session
|
||||
export NVM_DIR="${NVM_DIR}"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to install nvm"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Install Node.js
|
||||
install_nodejs_with_nvm() {
|
||||
local NODE_VERSION="${NODE_VERSION:-22}"
|
||||
local NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
|
||||
# Ensure nvm is loaded
|
||||
export NVM_DIR="${NVM_DIR}"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
|
||||
if ! command_exists nvm; then
|
||||
log_error "nvm not loaded properly"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if xz needs to be installed
|
||||
if ! command_exists xz; then
|
||||
log_warning "xz not found, trying to install xz-utils..."
|
||||
if command_exists yum; then
|
||||
sudo yum install -y xz || log_warning "Failed to install xz, continuing anyway..."
|
||||
elif command_exists apt-get; then
|
||||
sudo apt-get update && sudo apt-get install -y xz-utils || log_warning "Failed to install xz, continuing anyway..."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set Node.js mirror source (for domestic network)
|
||||
export NVM_NODEJS_ORG_MIRROR="https://npmmirror.com/mirrors/node"
|
||||
|
||||
# Clear cache
|
||||
log_info "Clearing nvm cache..."
|
||||
nvm cache clear || true
|
||||
|
||||
# Install Node.js
|
||||
log_info "Installing Node.js v${NODE_VERSION}..."
|
||||
if nvm install ${NODE_VERSION}; then
|
||||
nvm alias default ${NODE_VERSION}
|
||||
nvm use default
|
||||
log_success "Node.js v${NODE_VERSION} installed successfully"
|
||||
|
||||
# Verify installation
|
||||
log_info "Node.js version: $(node -v)"
|
||||
log_info "npm version: $(npm -v)"
|
||||
|
||||
# Clean npm configuration conflicts
|
||||
clean_npmrc_conflict
|
||||
|
||||
# Configure npm mirror source
|
||||
npm config set registry https://registry.npmmirror.com
|
||||
log_info "npm registry set to npmmirror"
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to install Node.js"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Node.js version
|
||||
check_node_version() {
|
||||
if ! command_exists node; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local current_version=$(node -v | sed 's/v//')
|
||||
local major_version=$(echo $current_version | cut -d. -f1)
|
||||
|
||||
if [ "$major_version" -ge 20 ]; then
|
||||
log_success "Node.js v$current_version is already installed (>= 20)"
|
||||
return 0
|
||||
else
|
||||
log_warning "Node.js v$current_version is installed but version < 20"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Install Node.js
|
||||
install_nodejs() {
|
||||
local platform=$(uname -s)
|
||||
|
||||
# Check if running in WSL (treat as Linux)
|
||||
if is_wsl; then
|
||||
log_info "WSL environment detected, treating as Linux..."
|
||||
platform="Linux"
|
||||
fi
|
||||
|
||||
case "$platform" in
|
||||
Linux|Darwin)
|
||||
log_info "Installing Node.js on $platform..."
|
||||
|
||||
# Install nvm
|
||||
if ! install_nvm; then
|
||||
log_error "Failed to install nvm"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Load nvm
|
||||
export NVM_DIR="${HOME}/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
|
||||
# Install Node.js
|
||||
if ! install_nodejs_with_nvm; then
|
||||
log_error "Failed to install Node.js"
|
||||
return 1
|
||||
fi
|
||||
|
||||
;;
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
log_warning "Windows platform detected (Git Bash/Cygwin/MSYS)"
|
||||
log_info "For Windows, we recommend:"
|
||||
log_info " 1. Install Node.js from https://nodejs.org/en/download/ (recommended)"
|
||||
log_info " 2. Or use WSL (Windows Subsystem for Linux)"
|
||||
log_info ""
|
||||
|
||||
# Check if Node.js is already installed
|
||||
if command_exists node; then
|
||||
local node_version=$(node -v)
|
||||
log_info "Node.js $node_version is already installed"
|
||||
if check_node_version; then
|
||||
log_success "Node.js version is compatible (>= 20)"
|
||||
return 0
|
||||
else
|
||||
log_warning "Node.js version is too old. Please upgrade from https://nodejs.org/en/download/"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "Node.js is not installed. Please install it from https://nodejs.org/en/download/"
|
||||
log_info "After installing Node.js, you can run this script again to install Qwen Code."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported platform: $platform"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check and update Node.js
|
||||
check_and_install_nodejs() {
|
||||
if check_node_version; then
|
||||
log_info "Using existing Node.js installation"
|
||||
clean_npmrc_conflict
|
||||
else
|
||||
log_warning "Installing or upgrading Node.js..."
|
||||
install_nodejs
|
||||
fi
|
||||
}
|
||||
|
||||
# Uninstall existing Qwen Code
|
||||
uninstall_existing_qwen_code() {
|
||||
local platform=$(uname -s)
|
||||
|
||||
# Check if running in WSL
|
||||
if is_wsl; then
|
||||
platform="Linux"
|
||||
fi
|
||||
|
||||
# Check if package is installed (even if command doesn't exist)
|
||||
local npm_prefix=$(npm config get prefix 2>/dev/null || echo "")
|
||||
local node_modules_dir=""
|
||||
|
||||
if [ -n "$npm_prefix" ]; then
|
||||
# Handle Windows paths
|
||||
case "$platform" in
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
# Windows: npm prefix might be like C:\Users\... or /c/Users/...
|
||||
node_modules_dir="$npm_prefix/node_modules/@qwen-code/qwen-code"
|
||||
;;
|
||||
*)
|
||||
# Unix-like: standard location
|
||||
node_modules_dir="$npm_prefix/lib/node_modules/@qwen-code/qwen-code"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# Try common locations
|
||||
case "$platform" in
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
# Windows locations
|
||||
local win_prefix=$(get_windows_npm_prefix)
|
||||
node_modules_dir="$win_prefix/node_modules/@qwen-code/qwen-code"
|
||||
;;
|
||||
*)
|
||||
# Unix-like locations
|
||||
if [ -d "$HOME/.nvm" ]; then
|
||||
# Find node version directory
|
||||
local node_version=$(node -v 2>/dev/null | sed 's/v//' || echo "")
|
||||
if [ -n "$node_version" ]; then
|
||||
node_modules_dir="$HOME/.nvm/versions/node/v${node_version}/lib/node_modules/@qwen-code/qwen-code"
|
||||
fi
|
||||
fi
|
||||
if [ -z "$node_modules_dir" ] || [ ! -d "$node_modules_dir" ]; then
|
||||
node_modules_dir="/usr/local/lib/node_modules/@qwen-code/qwen-code"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if command_exists qwen || [ -d "$node_modules_dir" ]; then
|
||||
log_warning "Existing Qwen Code installation detected"
|
||||
|
||||
# Try to get current version
|
||||
local current_version=$(qwen --version 2>/dev/null || echo "unknown")
|
||||
if [ "$current_version" != "unknown" ]; then
|
||||
log_info "Current version: $current_version"
|
||||
fi
|
||||
|
||||
log_info "Uninstalling existing Qwen Code..."
|
||||
|
||||
# Try npm uninstall first
|
||||
if npm uninstall -g @qwen-code/qwen-code 2>/dev/null; then
|
||||
log_success "Successfully uninstalled existing Qwen Code via npm"
|
||||
else
|
||||
log_warning "npm uninstall failed or returned non-zero, trying manual removal..."
|
||||
fi
|
||||
|
||||
# Always try to manually remove the module directory and binaries
|
||||
case "$platform" in
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
# Windows platform (Git Bash/Cygwin/MSYS)
|
||||
local win_npm_prefix=$(get_windows_npm_prefix)
|
||||
|
||||
# Windows binary locations
|
||||
local common_paths=()
|
||||
|
||||
# Try npm prefix locations
|
||||
if [ -n "$win_npm_prefix" ]; then
|
||||
common_paths+=(
|
||||
"$win_npm_prefix/qwen"
|
||||
"$win_npm_prefix/qwen.cmd"
|
||||
"$win_npm_prefix/qwen.ps1"
|
||||
)
|
||||
fi
|
||||
|
||||
# Try APPDATA if available
|
||||
if [ -n "$APPDATA" ]; then
|
||||
common_paths+=(
|
||||
"$APPDATA/npm/qwen"
|
||||
"$APPDATA/npm/qwen.cmd"
|
||||
"$APPDATA/npm/qwen.ps1"
|
||||
)
|
||||
fi
|
||||
|
||||
# Try Program Files
|
||||
if [ -d "/c/Program Files/nodejs" ]; then
|
||||
common_paths+=(
|
||||
"/c/Program Files/nodejs/qwen"
|
||||
"/c/Program Files/nodejs/qwen.cmd"
|
||||
)
|
||||
fi
|
||||
|
||||
# Remove binaries
|
||||
for bin_path in "${common_paths[@]}"; do
|
||||
if [ -f "$bin_path" ] || [ -L "$bin_path" ]; then
|
||||
rm -f "$bin_path" 2>/dev/null && log_info "Removed $bin_path" || log_warning "Could not remove $bin_path"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
*)
|
||||
# Unix-like platforms (Linux/macOS/WSL)
|
||||
local unix_npm_prefix=$(npm config get prefix 2>/dev/null || echo "$HOME/.npm-global")
|
||||
local bin_path="$unix_npm_prefix/bin/qwen"
|
||||
|
||||
# Remove qwen binary if exists
|
||||
if [ -f "$bin_path" ] || [ -L "$bin_path" ]; then
|
||||
rm -f "$bin_path" && log_info "Removed $bin_path"
|
||||
fi
|
||||
|
||||
# Remove from common Unix locations
|
||||
local common_paths=(
|
||||
"/usr/local/bin/qwen"
|
||||
"$HOME/.npm-global/bin/qwen"
|
||||
"$HOME/.local/bin/qwen"
|
||||
)
|
||||
|
||||
for path in "${common_paths[@]}"; do
|
||||
if [ -f "$path" ] || [ -L "$path" ]; then
|
||||
rm -f "$path" && log_info "Removed $path"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
for path in "${common_paths[@]}"; do
|
||||
if [ -f "$path" ]; then
|
||||
rm -f "$path" && log_info "Removed $path"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove the npm module directory if it exists
|
||||
if [ -n "$node_modules_dir" ] && [ -d "$node_modules_dir" ]; then
|
||||
log_info "Removing module directory: $node_modules_dir"
|
||||
rm -rf "$node_modules_dir" 2>/dev/null && log_info "Removed module directory" || log_warning "Could not remove module directory (may require sudo)"
|
||||
fi
|
||||
|
||||
# Also try to find and remove from nvm directories
|
||||
if [ -d "$HOME/.nvm" ]; then
|
||||
for nvm_node_dir in "$HOME/.nvm/versions/node"/*/lib/node_modules/@qwen-code/qwen-code; do
|
||||
if [ -d "$nvm_node_dir" ]; then
|
||||
log_info "Removing nvm module directory: $nvm_node_dir"
|
||||
rm -rf "$nvm_node_dir" 2>/dev/null && log_info "Removed nvm module directory" || log_warning "Could not remove nvm module directory"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Verify uninstallation
|
||||
if command_exists qwen; then
|
||||
log_warning "Qwen Code command still exists after uninstall attempt. Attempting to locate and remove it..."
|
||||
|
||||
# Find the qwen executable
|
||||
local qwen_path=$(which qwen 2>/dev/null)
|
||||
if [ -n "$qwen_path" ] && [ -f "$qwen_path" ]; then
|
||||
log_info "Found qwen executable at: $qwen_path"
|
||||
if rm -f "$qwen_path"; then
|
||||
log_success "Successfully removed qwen executable: $qwen_path"
|
||||
else
|
||||
log_error "Failed to remove qwen executable: $qwen_path (may require sudo)"
|
||||
fi
|
||||
else
|
||||
log_warning "Could not locate qwen executable path"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final check
|
||||
if command_exists qwen; then
|
||||
log_warning "Qwen Code command still exists. You may need to reload your shell."
|
||||
else
|
||||
log_success "Successfully removed existing Qwen Code"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Install Qwen Code
|
||||
install_qwen_code() {
|
||||
# Uninstall existing installation first
|
||||
uninstall_existing_qwen_code
|
||||
|
||||
# Additional cleanup: ensure module directory is completely removed
|
||||
local platform=$(uname -s)
|
||||
if is_wsl; then
|
||||
platform="Linux"
|
||||
fi
|
||||
|
||||
local npm_prefix=$(npm config get prefix 2>/dev/null || echo "")
|
||||
local module_dir=""
|
||||
|
||||
if [ -n "$npm_prefix" ]; then
|
||||
case "$platform" in
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
module_dir="$npm_prefix/node_modules/@qwen-code/qwen-code"
|
||||
;;
|
||||
*)
|
||||
module_dir="$npm_prefix/lib/node_modules/@qwen-code/qwen-code"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# Try to find node version directory
|
||||
case "$platform" in
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
local win_prefix=$(get_windows_npm_prefix)
|
||||
module_dir="$win_prefix/node_modules/@qwen-code/qwen-code"
|
||||
;;
|
||||
*)
|
||||
if [ -d "$HOME/.nvm" ]; then
|
||||
local node_version=$(node -v 2>/dev/null | sed 's/v//' || echo "")
|
||||
if [ -n "$node_version" ]; then
|
||||
module_dir="$HOME/.nvm/versions/node/v${node_version}/lib/node_modules/@qwen-code/qwen-code"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Force remove module directory if it still exists
|
||||
if [ -n "$module_dir" ] && [ -d "$module_dir" ]; then
|
||||
log_warning "Module directory still exists, forcing removal..."
|
||||
rm -rf "$module_dir" 2>/dev/null || {
|
||||
log_warning "Could not remove module directory. Trying with npm cache clean..."
|
||||
npm cache clean --force 2>/dev/null || true
|
||||
}
|
||||
fi
|
||||
|
||||
# Small delay to ensure filesystem operations complete
|
||||
sleep 1
|
||||
|
||||
log_info "Installing Qwen Code..."
|
||||
|
||||
# Install Qwen Code with force flag to handle any conflicts
|
||||
if npm i -g @qwen-code/qwen-code@latest --force; then
|
||||
log_success "Qwen Code installed successfully!"
|
||||
|
||||
# Verify installation
|
||||
if command_exists qwen; then
|
||||
log_info "Qwen Code version: $(qwen --version 2>/dev/null || echo 'version info not available')"
|
||||
else
|
||||
log_warning "Qwen Code installed but command not found. You may need to reload your shell or add npm global bin to PATH."
|
||||
log_info "Try running: export PATH=\"\$PATH:$(npm config get prefix)/bin\""
|
||||
fi
|
||||
else
|
||||
log_error "Failed to install Qwen Code!"
|
||||
log_info "You may need to manually remove the old installation:"
|
||||
if [ -n "$module_dir" ]; then
|
||||
log_info " rm -rf $module_dir"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " Qwen Code Installation Script"
|
||||
echo " One-Click Installation for Everyone"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check system
|
||||
local platform=$(uname -s)
|
||||
log_info "System: $platform $(uname -r)"
|
||||
log_info "Shell: $(basename "$SHELL")"
|
||||
|
||||
if is_wsl; then
|
||||
log_info "WSL (Windows Subsystem for Linux) detected"
|
||||
fi
|
||||
|
||||
if is_dev_machine; then
|
||||
log_info "Development machine environment detected"
|
||||
fi
|
||||
|
||||
# Windows-specific guidance
|
||||
case "$platform" in
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
log_info ""
|
||||
log_info "Note: You're running this script in Git Bash/Cygwin/MSYS"
|
||||
log_info "Make sure Node.js is installed and accessible from this environment"
|
||||
log_info ""
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check and install Node.js
|
||||
check_and_install_nodejs
|
||||
|
||||
# Ensure npm command is available
|
||||
if ! command_exists npm; then
|
||||
log_error "npm command not found after Node.js installation!"
|
||||
log_info "Please run: source $(get_shell_profile)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install Qwen Code
|
||||
install_qwen_code
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
log_success "Installation completed successfully!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
log_info "To start using Qwen Code, run:"
|
||||
local current_shell=$(basename "$SHELL")
|
||||
case "$current_shell" in
|
||||
bash)
|
||||
echo " source ~/.bashrc"
|
||||
;;
|
||||
zsh)
|
||||
echo " source ~/.zshrc"
|
||||
;;
|
||||
fish)
|
||||
echo " source ~/.config/fish/config.fish"
|
||||
;;
|
||||
*)
|
||||
echo " source ~/.profile # or reload your shell"
|
||||
;;
|
||||
esac
|
||||
echo " qwen"
|
||||
echo ""
|
||||
|
||||
# Try to run Qwen Code
|
||||
if command_exists qwen; then
|
||||
log_info "Qwen Code is ready to use!"
|
||||
log_info "Run 'qwen' to start, or visit https://github.com/QwenLM/qwen-code for more information."
|
||||
else
|
||||
log_info "Please reload your shell and run 'qwen' command."
|
||||
fi
|
||||
}
|
||||
|
||||
# Error handling
|
||||
trap 'log_error "An error occurred. Installation aborted."; exit 1' ERR
|
||||
|
||||
# Run main function
|
||||
main
|
||||
Reference in New Issue
Block a user