mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-30 15:59:09 +08:00
Add JavaScript execution support to code blocks
- Register new IPC channel and handler for JavaScript code execution - Extend code block execution to support JavaScript, TypeScript, and JS languages - Add JavaScript service with sandboxed execution using quickJs - Update UI to show JavaScript execution option alongside Python
This commit is contained in:
parent
f68f6e9896
commit
651e9a529e
@ -91,6 +91,9 @@ export enum IpcChannel {
|
||||
// Python
|
||||
Python_Execute = 'python:execute',
|
||||
|
||||
// JavaScript
|
||||
Js_Execute = 'js:execute',
|
||||
|
||||
//copilot
|
||||
Copilot_GetAuthMessage = 'copilot:get-auth-message',
|
||||
Copilot_GetCopilotToken = 'copilot:get-copilot-token',
|
||||
|
||||
@ -27,6 +27,7 @@ import DxtService from './services/DxtService'
|
||||
import { ExportService } from './services/ExportService'
|
||||
import { fileStorage as fileManager } from './services/FileStorage'
|
||||
import FileService from './services/FileSystemService'
|
||||
import { jsService } from './services/JsService'
|
||||
import KnowledgeService from './services/KnowledgeService'
|
||||
import mcpService from './services/MCPService'
|
||||
import MemoryService from './services/memory/MemoryService'
|
||||
@ -709,6 +710,11 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
||||
}
|
||||
)
|
||||
|
||||
// Register JavaScript execution handler
|
||||
ipcMain.handle(IpcChannel.Js_Execute, async (_, code: string, timeout?: number) => {
|
||||
return await jsService.executeScript(code, { timeout })
|
||||
})
|
||||
|
||||
ipcMain.handle(IpcChannel.App_IsBinaryExist, (_, name: string) => isBinaryExists(name))
|
||||
ipcMain.handle(IpcChannel.App_GetBinaryPath, (_, name: string) => getBinaryPath(name))
|
||||
ipcMain.handle(IpcChannel.App_InstallUvBinary, () => runInstallScript('install-uv.js'))
|
||||
|
||||
@ -343,6 +343,9 @@ const api = {
|
||||
execute: (script: string, context?: Record<string, any>, timeout?: number) =>
|
||||
ipcRenderer.invoke(IpcChannel.Python_Execute, script, context, timeout)
|
||||
},
|
||||
js: {
|
||||
execute: (code: string, timeout?: number) => ipcRenderer.invoke(IpcChannel.Js_Execute, code, timeout)
|
||||
},
|
||||
shell: {
|
||||
openExternal: (url: string, options?: Electron.OpenExternalOptions) => shell.openExternal(url, options)
|
||||
},
|
||||
|
||||
@ -87,7 +87,8 @@ export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave
|
||||
const [tools, setTools] = useState<ActionTool[]>([])
|
||||
|
||||
const isExecutable = useMemo(() => {
|
||||
return codeExecution.enabled && language === 'python'
|
||||
const executableLanguages = ['python', 'javascript', 'js', 'typescript', 'ts']
|
||||
return codeExecution.enabled && executableLanguages.includes(language.toLowerCase())
|
||||
}, [codeExecution.enabled, language])
|
||||
|
||||
const sourceViewRef = useRef<CodeEditorHandles>(null)
|
||||
@ -152,21 +153,49 @@ export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave
|
||||
setIsRunning(true)
|
||||
setExecutionResult(null)
|
||||
|
||||
pyodideService
|
||||
.runScript(children, {}, codeExecution.timeoutMinutes * 60000)
|
||||
.then((result) => {
|
||||
setExecutionResult(result)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Unexpected error:', error)
|
||||
setExecutionResult({
|
||||
text: `Unexpected error: ${error.message || 'Unknown error'}`
|
||||
const isPython = language === 'python'
|
||||
const isJavaScript = ['javascript', 'js', 'typescript', 'ts'].includes(language.toLowerCase())
|
||||
|
||||
if (isPython) {
|
||||
pyodideService
|
||||
.runScript(children, {}, codeExecution.timeoutMinutes * 60000)
|
||||
.then((result) => {
|
||||
setExecutionResult(result)
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
setIsRunning(false)
|
||||
})
|
||||
}, [children, codeExecution.timeoutMinutes])
|
||||
.catch((error) => {
|
||||
logger.error('Unexpected error:', error)
|
||||
setExecutionResult({
|
||||
text: `Unexpected error: ${error.message || 'Unknown error'}`
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
setIsRunning(false)
|
||||
})
|
||||
} else if (isJavaScript) {
|
||||
window.api.js
|
||||
.execute(children, codeExecution.timeoutMinutes * 60000)
|
||||
.then((result) => {
|
||||
if (result.error) {
|
||||
setExecutionResult({
|
||||
text: `Error: ${result.error}\n${result.stderr || ''}`
|
||||
})
|
||||
} else {
|
||||
setExecutionResult({
|
||||
text: result.stdout || (result.stderr ? `stderr: ${result.stderr}` : 'Execution completed')
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Unexpected error:', error)
|
||||
setExecutionResult({
|
||||
text: `Unexpected error: ${error.message || 'Unknown error'}`
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
setIsRunning(false)
|
||||
})
|
||||
}
|
||||
}, [children, codeExecution.timeoutMinutes, language])
|
||||
|
||||
const showPreviewTools = useMemo(() => {
|
||||
return viewMode !== 'source' && hasSpecialView
|
||||
|
||||
@ -3346,6 +3346,7 @@
|
||||
"dify_knowledge": "Dify 的 MCP 伺服器實現,提供了一個簡單的 API 來與 Dify 進行互動。需要配置 Dify Key",
|
||||
"fetch": "用於獲取 URL 網頁內容的 MCP 伺服器",
|
||||
"filesystem": "實現文件系統操作的模型上下文協議(MCP)的 Node.js 伺服器。需要配置允許訪問的目錄",
|
||||
"js": "在安全的沙盒環境中執行 JavaScript 程式碼。使用 quickJs 執行 JavaScript,支援大多數標準函式庫和流行的第三方函式庫",
|
||||
"mcp_auto_install": "自動安裝 MCP 服務(測試版)",
|
||||
"memory": "基於本地知識圖譜的持久性記憶基礎實現。這使得模型能夠在不同對話間記住使用者的相關資訊。需要配置 MEMORY_FILE_PATH 環境變數。",
|
||||
"no": "無描述",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user