Skip to content

Commit 871fe21

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/cherry-claw-agent
2 parents 52379e4 + 5f5ddb2 commit 871fe21

File tree

66 files changed

+1600
-276
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1600
-276
lines changed

.github/ISSUE_TEMPLATE/0_bug_report.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name: 🐛 Bug Report
22
description: Create a report to help us improve
3-
title: '[Bug]: '
4-
labels: ['BUG']
3+
title: "[Bug]: "
4+
labels: []
5+
type: Bug
56
body:
67
- type: markdown
78
attributes:

.github/ISSUE_TEMPLATE/1_feature_request.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name: 💡 Feature Request
22
description: Suggest an idea for this project
3-
title: '[Feature]: '
4-
labels: ['feature']
3+
title: "[Feature]: "
4+
labels: []
5+
type: Feature
56
body:
67
- type: markdown
78
attributes:

packages/aiCore/src/core/options/factory.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,3 @@ export function createGoogleOptions(options: ExtractProviderOptions<'google'>) {
114114
export function createOpenRouterOptions(options: ExtractProviderOptions<'openrouter'> | Record<string, any>) {
115115
return createProviderOptions('openrouter', options)
116116
}
117-
118-
/**
119-
* 创建XAI供应商选项的便捷函数
120-
*/
121-
export function createXaiOptions(options: ExtractProviderOptions<'xai'>) {
122-
return createProviderOptions('xai', options)
123-
}

packages/aiCore/src/core/plugins/built-in/webSearchPlugin/helper.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { anthropic } from '@ai-sdk/anthropic'
22
import { google } from '@ai-sdk/google'
33
import { openai } from '@ai-sdk/openai'
4+
import { xai } from '@ai-sdk/xai'
45
import type { InferToolInput, InferToolOutput } from 'ai'
56
import { type Tool } from 'ai'
67

7-
import { createOpenRouterOptions, createXaiOptions, mergeProviderOptions } from '../../../options'
8-
import type { ProviderOptionsMap } from '../../../options/types'
8+
import { createOpenRouterOptions, mergeProviderOptions } from '../../../options'
99
import type { AiRequestContext } from '../../'
1010
import type { OpenRouterSearchConfig } from './openrouter'
1111

@@ -16,14 +16,17 @@ export type OpenAISearchConfig = NonNullable<Parameters<typeof openai.tools.webS
1616
export type OpenAISearchPreviewConfig = NonNullable<Parameters<typeof openai.tools.webSearchPreview>[0]>
1717
export type AnthropicSearchConfig = NonNullable<Parameters<typeof anthropic.tools.webSearch_20250305>[0]>
1818
export type GoogleSearchConfig = NonNullable<Parameters<typeof google.tools.googleSearch>[0]>
19-
export type XAISearchConfig = NonNullable<ProviderOptionsMap['xai']['searchParameters']>
19+
export type XAIWebSearchConfig = NonNullable<Parameters<typeof xai.tools.webSearch>[0]>
20+
export type XAIXSearchConfig = NonNullable<Parameters<typeof xai.tools.xSearch>[0]>
2021

2122
type NormalizeTool<T> = T extends Tool<infer INPUT, infer OUTPUT> ? Tool<INPUT, OUTPUT> : Tool<any, any>
2223

2324
type AnthropicWebSearchTool = NormalizeTool<ReturnType<typeof anthropic.tools.webSearch_20250305>>
2425
type OpenAIWebSearchTool = NormalizeTool<ReturnType<typeof openai.tools.webSearch>>
2526
type OpenAIChatWebSearchTool = NormalizeTool<ReturnType<typeof openai.tools.webSearchPreview>>
2627
type GoogleWebSearchTool = NormalizeTool<ReturnType<typeof google.tools.googleSearch>>
28+
type XAIWebSearchTool = NormalizeTool<ReturnType<typeof xai.tools.webSearch>>
29+
type XAIXSearchTool = NormalizeTool<ReturnType<typeof xai.tools.xSearch>>
2730

2831
/**
2932
* 插件初始化时接收的完整配置对象
@@ -34,7 +37,8 @@ export interface WebSearchPluginConfig {
3437
openai?: OpenAISearchConfig
3538
'openai-chat'?: OpenAISearchPreviewConfig
3639
anthropic?: AnthropicSearchConfig
37-
xai?: ProviderOptionsMap['xai']['searchParameters']
40+
xai?: XAIWebSearchConfig
41+
'xai-xsearch'?: XAIXSearchConfig
3842
google?: GoogleSearchConfig
3943
openrouter?: OpenRouterSearchConfig
4044
}
@@ -47,10 +51,10 @@ export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchPluginConfig = {
4751
openai: {},
4852
'openai-chat': {},
4953
xai: {
50-
mode: 'on',
51-
returnCitations: true,
52-
maxSearchResults: 5,
53-
sources: [{ type: 'web' }, { type: 'x' }, { type: 'news' }]
54+
enableImageUnderstanding: true
55+
},
56+
'xai-xsearch': {
57+
enableImageUnderstanding: true
5458
},
5559
anthropic: {
5660
maxUses: 5
@@ -87,13 +91,18 @@ export type WebSearchToolOutputSchema = {
8791
web?: { uri: string; title: string }
8892
}>
8993
}
94+
// xAI 工具
95+
xai: InferToolOutput<XAIWebSearchTool>
96+
'xai-xsearch': InferToolOutput<XAIXSearchTool>
9097
}
9198

9299
export type WebSearchToolInputSchema = {
93100
anthropic: InferToolInput<AnthropicWebSearchTool>
94101
openai: InferToolInput<OpenAIWebSearchTool>
95102
google: InferToolInput<GoogleWebSearchTool>
96103
'openai-chat': InferToolInput<OpenAIChatWebSearchTool>
104+
xai: InferToolInput<XAIWebSearchTool>
105+
'xai-xsearch': InferToolInput<XAIXSearchTool>
97106
}
98107

99108
/**
@@ -141,8 +150,9 @@ export const switchWebSearchTool = (config: WebSearchPluginConfig, params: any,
141150
},
142151
xai: () => {
143152
const cfg = config.xai ?? DEFAULT_WEB_SEARCH_CONFIG.xai
144-
const searchOptions = createXaiOptions({ searchParameters: { ...cfg, mode: 'on' } })
145-
applyProviderOptionsSearch(params, searchOptions)
153+
applyToolBasedSearch(params, 'web_search', xai.tools.webSearch(cfg))
154+
const xSearchCfg = config['xai-xsearch'] ?? DEFAULT_WEB_SEARCH_CONFIG['xai-xsearch']
155+
applyToolBasedSearch(params, 'x_search', xai.tools.xSearch(xSearchCfg))
146156
},
147157
openrouter: () => {
148158
const cfg = (config.openrouter ?? DEFAULT_WEB_SEARCH_CONFIG.openrouter) as OpenRouterSearchConfig

packages/aiCore/src/core/providers/schemas.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,15 @@ export const baseProviders = [
101101
{
102102
id: 'xai',
103103
name: 'xAI (Grok)',
104-
creator: createXai,
104+
creator: (options: Parameters<typeof createXai>[0]) => {
105+
const provider = createXai(options)
106+
return customProvider({
107+
fallbackProvider: {
108+
...provider,
109+
languageModel: (modelId: string) => provider.responses(modelId)
110+
}
111+
})
112+
},
105113
supportsImageGeneration: true
106114
},
107115
{

src/main/services/CodeToolsService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,8 @@ class CodeToolsService {
12011201
detached: true,
12021202
stdio: 'ignore',
12031203
cwd: directory,
1204-
env: processEnv
1204+
env: processEnv,
1205+
shell: isWin
12051206
})
12061207

12071208
const successMessage = `Launched ${cliTool} in new terminal window`

src/main/services/OpenClawService.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,14 @@ class OpenClawService {
244244
const npmArgs = ['install', '-g', packageName]
245245
if (registryArg) npmArgs.push(registryArg)
246246

247-
// Keep the command string for logging and sudo retry
248-
const npmCommand = `"${npmPath}" install -g ${packageName} ${registryArg}`.trim()
247+
// Keep the command string for logging and sudo retry.
248+
// On macOS/Linux, prepend npm's parent dir to PATH so that sudo (which runs in a
249+
// clean environment without user PATH) can resolve `node` via npm's shebang
250+
// (#!/usr/bin/env node).
251+
const nodeDir = path.dirname(npmPath)
252+
const npmCommand = isWin
253+
? `"${npmPath}" install -g ${packageName} ${registryArg}`.trim()
254+
: `PATH="${nodeDir}:$PATH" "${npmPath}" install -g ${packageName} ${registryArg}`.trim()
249255

250256
// On Windows, wrap npm path in quotes if it contains spaces and is not already quoted
251257
const needsQuotes = isWin && npmPath.includes(' ') && !npmPath.startsWith('"')
@@ -347,8 +353,12 @@ class OpenClawService {
347353

348354
const npmArgs = ['uninstall', '-g', 'openclaw', '@qingchencloud/openclaw-zh']
349355

350-
// Keep the command string for logging and sudo retry
351-
const npmCommand = `"${npmPath}" uninstall -g openclaw @qingchencloud/openclaw-zh`
356+
// Keep the command string for logging and sudo retry.
357+
// On macOS/Linux, prepend npm's parent dir to PATH so that sudo can resolve `node`.
358+
const nodeDir = path.dirname(npmPath)
359+
const npmCommand = isWin
360+
? `"${npmPath}" uninstall -g openclaw @qingchencloud/openclaw-zh`
361+
: `PATH="${nodeDir}:$PATH" "${npmPath}" uninstall -g openclaw @qingchencloud/openclaw-zh`
352362

353363
// On Windows, wrap npm path in quotes if it contains spaces and is not already quoted
354364
const needsQuotes = isWin && npmPath.includes(' ') && !npmPath.startsWith('"')

src/main/services/agents/drizzle.config.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,12 @@
1818
* Drizzle Kit configuration for agents database
1919
*/
2020

21-
import os from 'node:os'
2221
import path from 'node:path'
2322

2423
import { defineConfig } from 'drizzle-kit'
2524
import { app } from 'electron'
2625

2726
function getDbPath() {
28-
if (process.env.NODE_ENV === 'development') {
29-
return path.join(os.homedir(), '.cherrystudio', 'data', 'agents.db')
30-
}
3127
return path.join(app.getPath('userData'), 'Data', 'agents.db')
3228
}
3329

src/main/services/agents/services/claudecode/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,37 @@ class ClaudeCodeService implements AgentServiceInterface {
169169
...(customGitBashPath ? { CLAUDE_CODE_GIT_BASH_PATH: customGitBashPath } : {})
170170
}
171171

172+
// Merge user-defined environment variables from session configuration
173+
const userEnvVars = session.configuration?.env_vars
174+
if (userEnvVars && typeof userEnvVars === 'object') {
175+
const BLOCKED_ENV_KEYS = new Set([
176+
'ANTHROPIC_API_KEY',
177+
'ANTHROPIC_AUTH_TOKEN',
178+
'ANTHROPIC_BASE_URL',
179+
'ANTHROPIC_MODEL',
180+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
181+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
182+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
183+
'ELECTRON_RUN_AS_NODE',
184+
'ELECTRON_NO_ATTACH_CONSOLE',
185+
'CLAUDE_CONFIG_DIR',
186+
'CLAUDE_CODE_USE_BEDROCK',
187+
'CLAUDE_CODE_GIT_BASH_PATH',
188+
'NODE_OPTIONS',
189+
'__PROTO__',
190+
'CONSTRUCTOR',
191+
'PROTOTYPE'
192+
])
193+
for (const [key, value] of Object.entries(userEnvVars)) {
194+
const upperKey = key.toUpperCase()
195+
if (BLOCKED_ENV_KEYS.has(upperKey)) {
196+
logger.warn('Blocked user env var override for system-critical variable', { key })
197+
} else if (typeof value === 'string') {
198+
env[key] = value
199+
}
200+
}
201+
}
202+
172203
const errorChunks: string[] = []
173204

174205
const sessionAllowedTools = new Set<string>(session.allowed_tools ?? [])

src/main/utils/ipService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export async function getIpCountry(): Promise<string> {
1313
const controller = new AbortController()
1414
const timeoutId = setTimeout(() => controller.abort(), 5000)
1515

16-
const ipinfo = await net.fetch(`https://api.ipinfo.io/lite/me?token=2a42580355dae4`, {
16+
const ipinfo = await net.fetch(`https://api.ipinfo.io/lite/me?token=5aa4105b40adbc`, {
1717
signal: controller.signal
1818
})
1919

0 commit comments

Comments
 (0)