@atom8n/n8n-nodes-langchain 2.5.6 → 2.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/known/nodes.json +8 -0
- package/dist/nodes/llms/LmChatCodexCli/LmChatCodexCli.node.js +456 -0
- package/dist/nodes/llms/LmChatCodexCli/LmChatCodexCli.node.js.map +1 -0
- package/dist/nodes/llms/LmChatCodexCli/codexCli.svg +1 -0
- package/dist/nodes/llms/LmChatCursorAgent/LmChatCursorAgent.node.js +46 -6
- package/dist/nodes/llms/LmChatCursorAgent/LmChatCursorAgent.node.js.map +1 -1
- package/dist/nodes/llms/LmChatOpenCodeCli/LmChatOpenCodeCli.node.js +443 -0
- package/dist/nodes/llms/LmChatOpenCodeCli/LmChatOpenCodeCli.node.js.map +1 -0
- package/dist/nodes/llms/LmChatOpenCodeCli/openCodeCli.svg +1 -0
- package/dist/types/nodes.json +3 -1
- package/package.json +13 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../nodes/llms/LmChatCursorAgent/LmChatCursorAgent.node.ts"],"sourcesContent":["import { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { AIMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport type { ChatResult } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { BindToolsInput } from '@langchain/core/language_models/chat_models';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport { N8nLlmTracing } from '../N8nLlmTracing';\nimport { spawn } from 'child_process';\n\ninterface CursorAgentFields {\n\tmodel: string;\n\tbinaryPath: string;\n\tworkingDirectory: string;\n}\n\ninterface ParsedToolCall {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n}\n\nconst TOOL_CALL_SYSTEM_PROMPT = `You have access to the following tools. When you need to call a tool, respond ONLY with a JSON block in this exact format (no other text before or after):\n\n\\`\\`\\`tool_calls\n[{\"id\": \"call_1\", \"name\": \"tool_name\", \"args\": {\"param\": \"value\"}}]\n\\`\\`\\`\n\nWhen you do NOT need to call a tool, respond normally with text. Never mix tool calls and text in the same response.\n\nAvailable tools:\n`;\n\n/**\n * Custom LangChain chat model that wraps the cursor-agent CLI binary.\n * Supports tool calling by injecting tool schemas into the prompt\n * and parsing structured JSON responses for tool calls.\n */\nclass ChatCursorAgentCLI extends BaseChatModel {\n\tmodel: string;\n\n\tbinaryPath: string;\n\n\tworkingDirectory: string;\n\n\tboundTools: BindToolsInput[] = [];\n\n\tconstructor(fields: CursorAgentFields) {\n\t\tsuper({});\n\t\tthis.model = fields.model;\n\t\tthis.binaryPath = fields.binaryPath;\n\t\tthis.workingDirectory = fields.workingDirectory;\n\t}\n\n\t_llmType(): string {\n\t\treturn 'cursor-agent-cli';\n\t}\n\n\toverride bindTools(tools: BindToolsInput[], kwargs?: Partial<this['ParsedCallOptions']>) {\n\t\tconst clone = new ChatCursorAgentCLI({\n\t\t\tmodel: this.model,\n\t\t\tbinaryPath: this.binaryPath,\n\t\t\tworkingDirectory: this.workingDirectory,\n\t\t});\n\t\tclone.boundTools = tools;\n\t\tclone.callbacks = this.callbacks;\n\t\tif (kwargs) {\n\t\t\treturn clone.bind(kwargs);\n\t\t}\n\t\treturn clone;\n\t}\n\n\tasync _generate(\n\t\tmessages: BaseMessage[],\n\t\t_options: this['ParsedCallOptions'],\n\t\t_runManager?: CallbackManagerForLLMRun,\n\t): Promise<ChatResult> {\n\t\t// If tools are bound, inject tool schemas into a system message\n\t\tconst processedMessages = [...messages];\n\t\tif (this.boundTools.length > 0) {\n\t\t\tconst toolDescriptions = this.boundTools\n\t\t\t\t.map((tool) => {\n\t\t\t\t\tconst t = tool as Record<string, unknown>;\n\t\t\t\t\tconst name = (t.name as string) ?? '';\n\t\t\t\t\tconst description = (t.description as string) ?? '';\n\t\t\t\t\tconst schema = t.parameters ?? t.schema ?? {};\n\t\t\t\t\treturn `- ${name}: ${description}\\n Parameters: ${JSON.stringify(schema)}`;\n\t\t\t\t})\n\t\t\t\t.join('\\n\\n');\n\n\t\t\tconst systemPrompt = TOOL_CALL_SYSTEM_PROMPT + toolDescriptions;\n\t\t\tprocessedMessages.unshift(new SystemMessage(systemPrompt));\n\t\t}\n\n\t\t// Build prompt from messages\n\t\tconst prompt = processedMessages\n\t\t\t.map((m) => {\n\t\t\t\tconst content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);\n\t\t\t\tif (m instanceof SystemMessage) return `[system]: ${content}`;\n\t\t\t\tif (m instanceof HumanMessage) return `[user]: ${content}`;\n\t\t\t\tif (m instanceof AIMessage) return `[assistant]: ${content}`;\n\t\t\t\treturn `[${m._getType()}]: ${content}`;\n\t\t\t})\n\t\t\t.join('\\n\\n');\n\n\t\t// Execute cursor-agent CLI\n\t\tconst rawResponse = await this.executeCursorAgent(prompt);\n\n\t\t// Check for tool calls in response\n\t\tif (this.boundTools.length > 0) {\n\t\t\tconst toolCalls = this.extractToolCalls(rawResponse);\n\t\t\tif (toolCalls.length > 0) {\n\t\t\t\tconst aiMessage = new AIMessage({\n\t\t\t\t\tcontent: '',\n\t\t\t\t\ttool_calls: toolCalls.map((tc) => ({\n\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\targs: tc.args,\n\t\t\t\t\t\ttype: 'tool_call' as const,\n\t\t\t\t\t})),\n\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\tgenerations: [{ message: aiMessage, text: '' }],\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Normal text response\n\t\tconst aiMessage = new AIMessage({ content: rawResponse });\n\t\treturn {\n\t\t\tgenerations: [{ message: aiMessage, text: rawResponse }],\n\t\t};\n\t}\n\n\tprivate extractToolCalls(text: string): ParsedToolCall[] {\n\t\t// Look for tool_calls JSON block\n\t\tconst toolCallRegex = /```tool_calls\\s*\\n([\\s\\S]*?)\\n```/;\n\t\tconst match = toolCallRegex.exec(text);\n\t\tif (!match) return [];\n\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(match[1]) as Array<{\n\t\t\t\tid?: string;\n\t\t\t\tname: string;\n\t\t\t\targs: Record<string, unknown>;\n\t\t\t}>;\n\t\t\tif (!Array.isArray(parsed)) return [];\n\n\t\t\treturn parsed.map((tc, i) => ({\n\t\t\t\tid: tc.id ?? `call_${i}`,\n\t\t\t\tname: tc.name,\n\t\t\t\targs: tc.args ?? {},\n\t\t\t}));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate async executeCursorAgent(prompt: string): Promise<string> {\n\t\tconst args = ['-p', '--output-format=stream-json', '--trust'];\n\t\tif (this.model && this.model !== 'auto') {\n\t\t\targs.push('--model', this.model);\n\t\t}\n\n\t\treturn await new Promise<string>((resolve, reject) => {\n\t\t\tconst child = spawn(this.binaryPath, args, {\n\t\t\t\tcwd: this.workingDirectory || undefined,\n\t\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\t\tenv: { ...process.env },\n\t\t\t});\n\n\t\t\tlet stdout = '';\n\t\t\tlet stderr = '';\n\n\t\t\tchild.stdout.on('data', (data: Buffer) => {\n\t\t\t\tstdout += data.toString();\n\t\t\t});\n\n\t\t\tchild.stderr.on('data', (data: Buffer) => {\n\t\t\t\tstderr += data.toString();\n\t\t\t});\n\n\t\t\tchild.on('error', (err: Error) => {\n\t\t\t\treject(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`Failed to spawn cursor-agent: ${err.message}. Make sure cursor-agent CLI is installed and accessible.`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tchild.on('close', (code: number | null) => {\n\t\t\t\tif (code !== 0 && !stdout) {\n\t\t\t\t\tconst errorMsg = stderr.trim() || `cursor-agent exited with code ${code}`;\n\t\t\t\t\treject(new Error(errorMsg));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst assistantContent = this.parseStreamJsonOutput(stdout);\n\n\t\t\t\tif (!assistantContent) {\n\t\t\t\t\treject(new Error('No assistant response received from cursor-agent'));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve(assistantContent);\n\t\t\t});\n\n\t\t\tif (child.stdin) {\n\t\t\t\tchild.stdin.write(prompt);\n\t\t\t\tchild.stdin.end();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate parseStreamJsonOutput(output: string): string {\n\t\tconst lines = output.split('\\n').filter((line) => line.trim());\n\t\tconst assistantParts: string[] = [];\n\n\t\tfor (const line of lines) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(line) as {\n\t\t\t\t\ttype?: string;\n\t\t\t\t\tmessage?: {\n\t\t\t\t\t\tcontent?: Array<{ type?: string; text?: string }>;\n\t\t\t\t\t};\n\t\t\t\t\ttext?: string;\n\t\t\t\t};\n\n\t\t\t\tif (parsed.type === 'assistant' && parsed.message?.content) {\n\t\t\t\t\tfor (const item of parsed.message.content) {\n\t\t\t\t\t\tif (item.type === 'text' && item.text) {\n\t\t\t\t\t\t\tassistantParts.push(item.text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip non-JSON lines\n\t\t\t}\n\t\t}\n\n\t\treturn assistantParts.join('');\n\t}\n}\n\nexport class LmChatCursorAgent implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Cursor Agent CLI Chat Model',\n\n\t\tname: 'lmChatCursorAgent',\n\t\ticon: 'file:cursorAgent.svg',\n\t\tgroup: ['transform'],\n\t\tversion: [1],\n\t\tdescription:\n\t\t\t'Chat model powered by the Cursor Agent CLI. Requires cursor-agent to be installed locally.',\n\t\tdefaults: {\n\t\t\tname: 'Cursor Agent CLI Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {},\n\t\t},\n\n\t\tinputs: [],\n\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription: 'The model to use via cursor-agent CLI.',\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'Auto', value: 'auto' },\n\t\t\t\t\t// Composer models\n\t\t\t\t\t{ name: 'Composer 2 Fast', value: 'composer-2-fast' },\n\t\t\t\t\t{ name: 'Composer 2', value: 'composer-2' },\n\t\t\t\t\t{ name: 'Composer 1.5', value: 'composer-1.5' },\n\t\t\t\t\t// Claude 4.6 models\n\t\t\t\t\t{ name: 'Claude 4.6 Opus High', value: 'claude-4.6-opus-high' },\n\t\t\t\t\t{ name: 'Claude 4.6 Opus High Thinking', value: 'claude-4.6-opus-high-thinking' },\n\t\t\t\t\t{ name: 'Claude 4.6 Opus Max', value: 'claude-4.6-opus-max' },\n\t\t\t\t\t{ name: 'Claude 4.6 Opus Max Thinking', value: 'claude-4.6-opus-max-thinking' },\n\t\t\t\t\t{ name: 'Claude 4.6 Sonnet Medium', value: 'claude-4.6-sonnet-medium' },\n\t\t\t\t\t{ name: 'Claude 4.6 Sonnet Medium Thinking', value: 'claude-4.6-sonnet-medium-thinking' },\n\t\t\t\t\t// Claude 4.5 models\n\t\t\t\t\t{ name: 'Claude 4.5 Opus High', value: 'claude-4.5-opus-high' },\n\t\t\t\t\t{ name: 'Claude 4.5 Opus High Thinking', value: 'claude-4.5-opus-high-thinking' },\n\t\t\t\t\t{ name: 'Claude 4.5 Sonnet', value: 'claude-4.5-sonnet' },\n\t\t\t\t\t{ name: 'Claude 4.5 Sonnet Thinking', value: 'claude-4.5-sonnet-thinking' },\n\t\t\t\t\t// Claude 4 models\n\t\t\t\t\t{ name: 'Claude 4 Sonnet', value: 'claude-4-sonnet' },\n\t\t\t\t\t{ name: 'Claude 4 Sonnet 1M', value: 'claude-4-sonnet-1m' },\n\t\t\t\t\t{ name: 'Claude 4 Sonnet Thinking', value: 'claude-4-sonnet-thinking' },\n\t\t\t\t\t{ name: 'Claude 4 Sonnet 1M Thinking', value: 'claude-4-sonnet-1m-thinking' },\n\t\t\t\t\t// Gemini models\n\t\t\t\t\t{ name: 'Gemini 3.1 Pro', value: 'gemini-3.1-pro' },\n\t\t\t\t\t{ name: 'Gemini 3 Flash', value: 'gemini-3-flash' },\n\t\t\t\t\t// GPT-5.4 models\n\t\t\t\t\t{ name: 'GPT-5.4 Low', value: 'gpt-5.4-low' },\n\t\t\t\t\t{ name: 'GPT-5.4 Medium', value: 'gpt-5.4-medium' },\n\t\t\t\t\t{ name: 'GPT-5.4 Medium Fast', value: 'gpt-5.4-medium-fast' },\n\t\t\t\t\t{ name: 'GPT-5.4 High', value: 'gpt-5.4-high' },\n\t\t\t\t\t{ name: 'GPT-5.4 High Fast', value: 'gpt-5.4-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.4 XHigh', value: 'gpt-5.4-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.4 XHigh Fast', value: 'gpt-5.4-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini None', value: 'gpt-5.4-mini-none' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini Low', value: 'gpt-5.4-mini-low' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini Medium', value: 'gpt-5.4-mini-medium' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini High', value: 'gpt-5.4-mini-high' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini XHigh', value: 'gpt-5.4-mini-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano None', value: 'gpt-5.4-nano-none' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano Low', value: 'gpt-5.4-nano-low' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano Medium', value: 'gpt-5.4-nano-medium' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano High', value: 'gpt-5.4-nano-high' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano XHigh', value: 'gpt-5.4-nano-xhigh' },\n\t\t\t\t\t// GPT-5.3 Codex models\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Low', value: 'gpt-5.3-codex-low' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Low Fast', value: 'gpt-5.3-codex-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex', value: 'gpt-5.3-codex' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Fast', value: 'gpt-5.3-codex-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex High', value: 'gpt-5.3-codex-high' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex High Fast', value: 'gpt-5.3-codex-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex XHigh', value: 'gpt-5.3-codex-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex XHigh Fast', value: 'gpt-5.3-codex-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview Low', value: 'gpt-5.3-codex-spark-preview-low' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview', value: 'gpt-5.3-codex-spark-preview' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview High', value: 'gpt-5.3-codex-spark-preview-high' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview XHigh', value: 'gpt-5.3-codex-spark-preview-xhigh' },\n\t\t\t\t\t// GPT-5.2 models\n\t\t\t\t\t{ name: 'GPT-5.2 Low', value: 'gpt-5.2-low' },\n\t\t\t\t\t{ name: 'GPT-5.2 Low Fast', value: 'gpt-5.2-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2', value: 'gpt-5.2' },\n\t\t\t\t\t{ name: 'GPT-5.2 Fast', value: 'gpt-5.2-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 High', value: 'gpt-5.2-high' },\n\t\t\t\t\t{ name: 'GPT-5.2 High Fast', value: 'gpt-5.2-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 XHigh', value: 'gpt-5.2-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.2 XHigh Fast', value: 'gpt-5.2-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex Low', value: 'gpt-5.2-codex-low' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex Low Fast', value: 'gpt-5.2-codex-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex', value: 'gpt-5.2-codex' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex Fast', value: 'gpt-5.2-codex-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex High', value: 'gpt-5.2-codex-high' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex High Fast', value: 'gpt-5.2-codex-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex XHigh', value: 'gpt-5.2-codex-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex XHigh Fast', value: 'gpt-5.2-codex-xhigh-fast' },\n\t\t\t\t\t// GPT-5.1 models\n\t\t\t\t\t{ name: 'GPT-5.1 Low', value: 'gpt-5.1-low' },\n\t\t\t\t\t{ name: 'GPT-5.1', value: 'gpt-5.1' },\n\t\t\t\t\t{ name: 'GPT-5.1 High', value: 'gpt-5.1-high' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Low', value: 'gpt-5.1-codex-max-low' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Low Fast', value: 'gpt-5.1-codex-max-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Medium', value: 'gpt-5.1-codex-max-medium' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Medium Fast', value: 'gpt-5.1-codex-max-medium-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max High', value: 'gpt-5.1-codex-max-high' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max High Fast', value: 'gpt-5.1-codex-max-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max XHigh', value: 'gpt-5.1-codex-max-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max XHigh Fast', value: 'gpt-5.1-codex-max-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Mini Low', value: 'gpt-5.1-codex-mini-low' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Mini', value: 'gpt-5.1-codex-mini' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Mini High', value: 'gpt-5.1-codex-mini-high' },\n\t\t\t\t\t// GPT-5 models\n\t\t\t\t\t{ name: 'GPT-5 Mini', value: 'gpt-5-mini' },\n\t\t\t\t\t// Grok models\n\t\t\t\t\t{ name: 'Grok 4 20', value: 'grok-4-20' },\n\t\t\t\t\t{ name: 'Grok 4 20 Thinking', value: 'grok-4-20-thinking' },\n\t\t\t\t\t// Kimi models\n\t\t\t\t\t{ name: 'Kimi K2.5', value: 'kimi-k2.5' },\n\t\t\t\t],\n\t\t\t\tdefault: 'auto',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to configure',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Binary Path',\n\t\t\t\t\t\tname: 'binaryPath',\n\t\t\t\t\t\tdefault: 'cursor-agent',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Path to the cursor-agent binary. Defaults to \"cursor-agent\" (must be in PATH).',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Working Directory',\n\t\t\t\t\t\tname: 'workingDirectory',\n\t\t\t\t\t\tdefault: '',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Working directory for the cursor-agent process. Leave empty to use the default.',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst modelName = this.getNodeParameter('model', itemIndex) as string;\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tbinaryPath?: string;\n\t\t\tworkingDirectory?: string;\n\t\t};\n\n\t\tconst model = new ChatCursorAgentCLI({\n\t\t\tmodel: modelName,\n\t\t\tbinaryPath: options.binaryPath ?? 'cursor-agent',\n\t\t\tworkingDirectory: options.workingDirectory ?? '',\n\t\t});\n\n\t\tmodel.callbacks = [new N8nLlmTracing(this)];\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":["aiMessage"]}
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/llms/LmChatCursorAgent/LmChatCursorAgent.node.ts"],"sourcesContent":["import { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { AIMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport type { ChatResult } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { BindToolsInput } from '@langchain/core/language_models/chat_models';\nimport {\n\tApplicationError,\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport { N8nLlmTracing } from '../N8nLlmTracing';\nimport { spawn } from 'child_process';\nimport { existsSync, statSync } from 'fs';\n\ninterface CursorAgentFields {\n\tmodel: string;\n\tbinaryPath: string;\n\tworkingDirectory: string;\n}\n\ninterface ParsedToolCall {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n}\n\nconst TOOL_CALL_SYSTEM_PROMPT = `You have access to the following tools. When you need to call a tool, respond ONLY with a JSON block in this exact format (no other text before or after):\n\n\\`\\`\\`tool_calls\n[{\"id\": \"call_1\", \"name\": \"tool_name\", \"args\": {\"param\": \"value\"}}]\n\\`\\`\\`\n\nWhen you do NOT need to call a tool, respond normally with text. Never mix tool calls and text in the same response.\n\nAvailable tools:\n`;\n\n/**\n * Custom LangChain chat model that wraps the cursor-agent CLI binary.\n * Supports tool calling by injecting tool schemas into the prompt\n * and parsing structured JSON responses for tool calls.\n */\nclass ChatCursorAgentCLI extends BaseChatModel {\n\tmodel: string;\n\n\tbinaryPath: string;\n\n\tworkingDirectory: string;\n\n\tboundTools: BindToolsInput[] = [];\n\n\tconstructor(fields: CursorAgentFields) {\n\t\tsuper({});\n\t\tthis.model = fields.model;\n\t\tthis.binaryPath = fields.binaryPath;\n\t\tthis.workingDirectory = fields.workingDirectory;\n\t}\n\n\t_llmType(): string {\n\t\treturn 'cursor-agent-cli';\n\t}\n\n\toverride bindTools(tools: BindToolsInput[], kwargs?: Partial<this['ParsedCallOptions']>) {\n\t\tconst clone = new ChatCursorAgentCLI({\n\t\t\tmodel: this.model,\n\t\t\tbinaryPath: this.binaryPath,\n\t\t\tworkingDirectory: this.workingDirectory,\n\t\t});\n\t\tclone.boundTools = tools;\n\t\tclone.callbacks = this.callbacks;\n\t\tif (kwargs) {\n\t\t\treturn (\n\t\t\t\tclone as unknown as {\n\t\t\t\t\tbind: (kwargs: Record<string, unknown>) => ChatCursorAgentCLI;\n\t\t\t\t}\n\t\t\t).bind(kwargs as Record<string, unknown>);\n\t\t}\n\t\treturn clone;\n\t}\n\n\tasync _generate(\n\t\tmessages: BaseMessage[],\n\t\t_options: this['ParsedCallOptions'],\n\t\t_runManager?: CallbackManagerForLLMRun,\n\t): Promise<ChatResult> {\n\t\t// If tools are bound, inject tool schemas into a system message\n\t\tconst processedMessages = [...messages];\n\t\tif (this.boundTools.length > 0) {\n\t\t\tconst toolDescriptions = this.boundTools\n\t\t\t\t.map((tool) => {\n\t\t\t\t\tconst t = tool as Record<string, unknown>;\n\t\t\t\t\tconst name = (t.name as string) ?? '';\n\t\t\t\t\tconst description = (t.description as string) ?? '';\n\t\t\t\t\tconst schema = t.parameters ?? t.schema ?? {};\n\t\t\t\t\treturn `- ${name}: ${description}\\n Parameters: ${JSON.stringify(schema)}`;\n\t\t\t\t})\n\t\t\t\t.join('\\n\\n');\n\n\t\t\tconst systemPrompt = TOOL_CALL_SYSTEM_PROMPT + toolDescriptions;\n\t\t\tprocessedMessages.unshift(new SystemMessage(systemPrompt));\n\t\t}\n\n\t\t// Build prompt from messages\n\t\tconst prompt = processedMessages\n\t\t\t.map((m) => {\n\t\t\t\tconst content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);\n\t\t\t\tif (m instanceof SystemMessage) return `[system]: ${content}`;\n\t\t\t\tif (m instanceof HumanMessage) return `[user]: ${content}`;\n\t\t\t\tif (m instanceof AIMessage) return `[assistant]: ${content}`;\n\t\t\t\treturn `[${m._getType()}]: ${content}`;\n\t\t\t})\n\t\t\t.join('\\n\\n');\n\n\t\t// Execute cursor-agent CLI\n\t\tconst rawResponse = await this.executeCursorAgent(prompt);\n\n\t\t// Check for tool calls in response\n\t\tif (this.boundTools.length > 0) {\n\t\t\tconst toolCalls = this.extractToolCalls(rawResponse);\n\t\t\tif (toolCalls.length > 0) {\n\t\t\t\tconst aiMessage = new AIMessage({\n\t\t\t\t\tcontent: '',\n\t\t\t\t\ttool_calls: toolCalls.map((tc) => ({\n\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\targs: tc.args,\n\t\t\t\t\t\ttype: 'tool_call' as const,\n\t\t\t\t\t})),\n\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\tgenerations: [{ message: aiMessage, text: '' }],\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Normal text response\n\t\tconst aiMessage = new AIMessage({ content: rawResponse });\n\t\treturn {\n\t\t\tgenerations: [{ message: aiMessage, text: rawResponse }],\n\t\t};\n\t}\n\n\tprivate extractToolCalls(text: string): ParsedToolCall[] {\n\t\t// Look for tool_calls JSON block\n\t\tconst toolCallRegex = /```tool_calls\\s*\\n([\\s\\S]*?)\\n```/;\n\t\tconst match = toolCallRegex.exec(text);\n\t\tif (!match) return [];\n\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(match[1]) as Array<{\n\t\t\t\tid?: string;\n\t\t\t\tname: string;\n\t\t\t\targs: Record<string, unknown>;\n\t\t\t}>;\n\t\t\tif (!Array.isArray(parsed)) return [];\n\n\t\t\treturn parsed.map((tc, i) => ({\n\t\t\t\tid: tc.id ?? `call_${i}`,\n\t\t\t\tname: tc.name,\n\t\t\t\targs: tc.args ?? {},\n\t\t\t}));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate async executeCursorAgent(prompt: string): Promise<string> {\n\t\tconst args = ['-p', '--output-format=stream-json', '--trust'];\n\t\tif (this.model && this.model !== 'auto') {\n\t\t\targs.push('--model', this.model);\n\t\t}\n\t\tconst cwd = this.workingDirectory?.trim() || undefined;\n\n\t\tconsole.log('[LmChatCursorAgent] spawning cursor-agent', {\n\t\t\tbinaryPath: this.binaryPath,\n\t\t\tmodel: this.model,\n\t\t\tcwd,\n\t\t});\n\n\t\treturn await new Promise<string>((resolve, reject) => {\n\t\t\tconst child = spawn(this.binaryPath, args, {\n\t\t\t\tcwd,\n\t\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\t\tenv: { ...process.env },\n\t\t\t});\n\n\t\t\tlet stdout = '';\n\t\t\tlet stderr = '';\n\n\t\t\tchild.stdout.on('data', (data: Buffer) => {\n\t\t\t\tstdout += data.toString();\n\t\t\t});\n\n\t\t\tchild.stderr.on('data', (data: Buffer) => {\n\t\t\t\tstderr += data.toString();\n\t\t\t});\n\n\t\t\tchild.on('error', (err: Error) => {\n\t\t\t\treject(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`Failed to spawn cursor-agent: ${err.message}. Make sure cursor-agent CLI is installed and accessible. Working directory: ${cwd ?? '<default>'}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tchild.on('close', (code: number | null) => {\n\t\t\t\tif (code !== 0 && !stdout) {\n\t\t\t\t\tconst errorMsg = stderr.trim() || `cursor-agent exited with code ${code}`;\n\t\t\t\t\treject(new Error(errorMsg));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst assistantContent = this.parseStreamJsonOutput(stdout);\n\n\t\t\t\tif (!assistantContent) {\n\t\t\t\t\treject(new Error('No assistant response received from cursor-agent'));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve(assistantContent);\n\t\t\t});\n\n\t\t\tif (child.stdin) {\n\t\t\t\tchild.stdin.write(prompt);\n\t\t\t\tchild.stdin.end();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate parseStreamJsonOutput(output: string): string {\n\t\tconst lines = output.split('\\n').filter((line) => line.trim());\n\t\tconst assistantParts: string[] = [];\n\n\t\tfor (const line of lines) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(line) as {\n\t\t\t\t\ttype?: string;\n\t\t\t\t\tmessage?: {\n\t\t\t\t\t\tcontent?: Array<{ type?: string; text?: string }>;\n\t\t\t\t\t};\n\t\t\t\t\ttext?: string;\n\t\t\t\t};\n\n\t\t\t\tif (parsed.type === 'assistant' && parsed.message?.content) {\n\t\t\t\t\tfor (const item of parsed.message.content) {\n\t\t\t\t\t\tif (item.type === 'text' && item.text) {\n\t\t\t\t\t\t\tassistantParts.push(item.text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip non-JSON lines\n\t\t\t}\n\t\t}\n\n\t\treturn assistantParts.join('');\n\t}\n}\n\nexport class LmChatCursorAgent implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Cursor Agent CLI Chat Model',\n\n\t\tname: 'lmChatCursorAgent',\n\t\ticon: 'file:cursorAgent.svg',\n\t\tgroup: ['transform'],\n\t\tversion: [1],\n\t\tdescription:\n\t\t\t'Chat model powered by the Cursor Agent CLI. Requires cursor-agent to be installed locally.',\n\t\tdefaults: {\n\t\t\tname: 'Cursor Agent CLI Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {},\n\t\t},\n\n\t\tinputs: [],\n\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription: 'The model to use via cursor-agent CLI',\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'Auto', value: 'auto' },\n\t\t\t\t\t// Composer models\n\t\t\t\t\t{ name: 'Composer 2 Fast', value: 'composer-2-fast' },\n\t\t\t\t\t{ name: 'Composer 2', value: 'composer-2' },\n\t\t\t\t\t{ name: 'Composer 1.5', value: 'composer-1.5' },\n\t\t\t\t\t// Claude 4.6 models\n\t\t\t\t\t{ name: 'Claude 4.6 Opus High', value: 'claude-4.6-opus-high' },\n\t\t\t\t\t{ name: 'Claude 4.6 Opus High Thinking', value: 'claude-4.6-opus-high-thinking' },\n\t\t\t\t\t{ name: 'Claude 4.6 Opus Max', value: 'claude-4.6-opus-max' },\n\t\t\t\t\t{ name: 'Claude 4.6 Opus Max Thinking', value: 'claude-4.6-opus-max-thinking' },\n\t\t\t\t\t{ name: 'Claude 4.6 Sonnet Medium', value: 'claude-4.6-sonnet-medium' },\n\t\t\t\t\t{ name: 'Claude 4.6 Sonnet Medium Thinking', value: 'claude-4.6-sonnet-medium-thinking' },\n\t\t\t\t\t// Claude 4.5 models\n\t\t\t\t\t{ name: 'Claude 4.5 Opus High', value: 'claude-4.5-opus-high' },\n\t\t\t\t\t{ name: 'Claude 4.5 Opus High Thinking', value: 'claude-4.5-opus-high-thinking' },\n\t\t\t\t\t{ name: 'Claude 4.5 Sonnet', value: 'claude-4.5-sonnet' },\n\t\t\t\t\t{ name: 'Claude 4.5 Sonnet Thinking', value: 'claude-4.5-sonnet-thinking' },\n\t\t\t\t\t// Claude 4 models\n\t\t\t\t\t{ name: 'Claude 4 Sonnet', value: 'claude-4-sonnet' },\n\t\t\t\t\t{ name: 'Claude 4 Sonnet 1M', value: 'claude-4-sonnet-1m' },\n\t\t\t\t\t{ name: 'Claude 4 Sonnet Thinking', value: 'claude-4-sonnet-thinking' },\n\t\t\t\t\t{ name: 'Claude 4 Sonnet 1M Thinking', value: 'claude-4-sonnet-1m-thinking' },\n\t\t\t\t\t// Gemini models\n\t\t\t\t\t{ name: 'Gemini 3.1 Pro', value: 'gemini-3.1-pro' },\n\t\t\t\t\t{ name: 'Gemini 3 Flash', value: 'gemini-3-flash' },\n\t\t\t\t\t// GPT-5.4 models\n\t\t\t\t\t{ name: 'GPT-5.4 Low', value: 'gpt-5.4-low' },\n\t\t\t\t\t{ name: 'GPT-5.4 Medium', value: 'gpt-5.4-medium' },\n\t\t\t\t\t{ name: 'GPT-5.4 Medium Fast', value: 'gpt-5.4-medium-fast' },\n\t\t\t\t\t{ name: 'GPT-5.4 High', value: 'gpt-5.4-high' },\n\t\t\t\t\t{ name: 'GPT-5.4 High Fast', value: 'gpt-5.4-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.4 XHigh', value: 'gpt-5.4-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.4 XHigh Fast', value: 'gpt-5.4-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini None', value: 'gpt-5.4-mini-none' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini Low', value: 'gpt-5.4-mini-low' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini Medium', value: 'gpt-5.4-mini-medium' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini High', value: 'gpt-5.4-mini-high' },\n\t\t\t\t\t{ name: 'GPT-5.4 Mini XHigh', value: 'gpt-5.4-mini-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano None', value: 'gpt-5.4-nano-none' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano Low', value: 'gpt-5.4-nano-low' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano Medium', value: 'gpt-5.4-nano-medium' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano High', value: 'gpt-5.4-nano-high' },\n\t\t\t\t\t{ name: 'GPT-5.4 Nano XHigh', value: 'gpt-5.4-nano-xhigh' },\n\t\t\t\t\t// GPT-5.3 Codex models\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Low', value: 'gpt-5.3-codex-low' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Low Fast', value: 'gpt-5.3-codex-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex', value: 'gpt-5.3-codex' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Fast', value: 'gpt-5.3-codex-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex High', value: 'gpt-5.3-codex-high' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex High Fast', value: 'gpt-5.3-codex-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex XHigh', value: 'gpt-5.3-codex-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex XHigh Fast', value: 'gpt-5.3-codex-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview Low', value: 'gpt-5.3-codex-spark-preview-low' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview', value: 'gpt-5.3-codex-spark-preview' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview High', value: 'gpt-5.3-codex-spark-preview-high' },\n\t\t\t\t\t{ name: 'GPT-5.3 Codex Spark Preview XHigh', value: 'gpt-5.3-codex-spark-preview-xhigh' },\n\t\t\t\t\t// GPT-5.2 models\n\t\t\t\t\t{ name: 'GPT-5.2 Low', value: 'gpt-5.2-low' },\n\t\t\t\t\t{ name: 'GPT-5.2 Low Fast', value: 'gpt-5.2-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2', value: 'gpt-5.2' },\n\t\t\t\t\t{ name: 'GPT-5.2 Fast', value: 'gpt-5.2-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 High', value: 'gpt-5.2-high' },\n\t\t\t\t\t{ name: 'GPT-5.2 High Fast', value: 'gpt-5.2-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 XHigh', value: 'gpt-5.2-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.2 XHigh Fast', value: 'gpt-5.2-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex Low', value: 'gpt-5.2-codex-low' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex Low Fast', value: 'gpt-5.2-codex-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex', value: 'gpt-5.2-codex' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex Fast', value: 'gpt-5.2-codex-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex High', value: 'gpt-5.2-codex-high' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex High Fast', value: 'gpt-5.2-codex-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex XHigh', value: 'gpt-5.2-codex-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.2 Codex XHigh Fast', value: 'gpt-5.2-codex-xhigh-fast' },\n\t\t\t\t\t// GPT-5.1 models\n\t\t\t\t\t{ name: 'GPT-5.1 Low', value: 'gpt-5.1-low' },\n\t\t\t\t\t{ name: 'GPT-5.1', value: 'gpt-5.1' },\n\t\t\t\t\t{ name: 'GPT-5.1 High', value: 'gpt-5.1-high' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Low', value: 'gpt-5.1-codex-max-low' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Low Fast', value: 'gpt-5.1-codex-max-low-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Medium', value: 'gpt-5.1-codex-max-medium' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max Medium Fast', value: 'gpt-5.1-codex-max-medium-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max High', value: 'gpt-5.1-codex-max-high' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max High Fast', value: 'gpt-5.1-codex-max-high-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max XHigh', value: 'gpt-5.1-codex-max-xhigh' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Max XHigh Fast', value: 'gpt-5.1-codex-max-xhigh-fast' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Mini Low', value: 'gpt-5.1-codex-mini-low' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Mini', value: 'gpt-5.1-codex-mini' },\n\t\t\t\t\t{ name: 'GPT-5.1 Codex Mini High', value: 'gpt-5.1-codex-mini-high' },\n\t\t\t\t\t// GPT-5 models\n\t\t\t\t\t{ name: 'GPT-5 Mini', value: 'gpt-5-mini' },\n\t\t\t\t\t// Grok models\n\t\t\t\t\t{ name: 'Grok 4 20', value: 'grok-4-20' },\n\t\t\t\t\t{ name: 'Grok 4 20 Thinking', value: 'grok-4-20-thinking' },\n\t\t\t\t\t// Kimi models\n\t\t\t\t\t{ name: 'Kimi K2.5', value: 'kimi-k2.5' },\n\t\t\t\t],\n\t\t\t\tdefault: 'auto',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to configure',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Binary Path',\n\t\t\t\t\t\tname: 'binaryPath',\n\t\t\t\t\t\tdefault: 'cursor-agent',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Path to the cursor-agent binary. Defaults to \"cursor-agent\" (must be in PATH).',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Working Directory',\n\t\t\t\t\t\tname: 'workingDirectory',\n\t\t\t\t\t\tdefault: '',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Working directory for the cursor-agent process. Leave empty to use the default.',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst modelName = this.getNodeParameter('model', itemIndex) as string;\n\n\t\tconst binaryPath = this.getNodeParameter(\n\t\t\t'options.binaryPath',\n\t\t\titemIndex,\n\t\t\t'cursor-agent',\n\t\t) as string;\n\t\tconst rawWorkingDirectory = this.getNodeParameter('options.workingDirectory', itemIndex, '', {\n\t\t\trawExpressions: true,\n\t\t}) as string | undefined;\n\t\tconst workingDirectory = this.getNodeParameter('options.workingDirectory', itemIndex, '') as\n\t\t\t| string\n\t\t\t| undefined;\n\t\tconst normalizedWorkingDirectory = (workingDirectory ?? '').trim();\n\t\tconst rawWorkingDirectoryValue = rawWorkingDirectory ?? '';\n\t\tconst isWorkingDirectoryExpression =\n\t\t\trawWorkingDirectoryValue.startsWith('=') ||\n\t\t\trawWorkingDirectoryValue.includes('{{') ||\n\t\t\trawWorkingDirectoryValue.includes('$workspace');\n\n\t\tconsole.log('[LmChatCursorAgent] resolved Cursor Agent options', {\n\t\t\titemIndex,\n\t\t\tmodelName,\n\t\t\tbinaryPath,\n\t\t\trawWorkingDirectory,\n\t\t\tworkingDirectory: normalizedWorkingDirectory,\n\t\t});\n\n\t\tif (isWorkingDirectoryExpression && !normalizedWorkingDirectory) {\n\t\t\tthrow new ApplicationError(\n\t\t\t\t`Cursor Agent working directory expression resolved to an empty value: ${rawWorkingDirectoryValue}`,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tnormalizedWorkingDirectory.includes('{{') ||\n\t\t\tnormalizedWorkingDirectory.includes('$workspace')\n\t\t) {\n\t\t\tthrow new ApplicationError(\n\t\t\t\t`Cursor Agent working directory was not resolved before execution: ${normalizedWorkingDirectory}`,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tnormalizedWorkingDirectory &&\n\t\t\t(!existsSync(normalizedWorkingDirectory) ||\n\t\t\t\t!statSync(normalizedWorkingDirectory).isDirectory())\n\t\t) {\n\t\t\tthrow new ApplicationError(\n\t\t\t\t`Cursor Agent working directory does not exist or is not a directory: ${normalizedWorkingDirectory}`,\n\t\t\t);\n\t\t}\n\n\t\tconst model = new ChatCursorAgentCLI({\n\t\t\tmodel: modelName,\n\t\t\tbinaryPath,\n\t\t\tworkingDirectory: normalizedWorkingDirectory,\n\t\t});\n\n\t\tmodel.callbacks = [new N8nLlmTracing(this)];\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":["aiMessage"]}
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var LmChatOpenCodeCli_node_exports = {};
|
|
20
|
+
__export(LmChatOpenCodeCli_node_exports, {
|
|
21
|
+
LmChatOpenCodeCli: () => LmChatOpenCodeCli
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(LmChatOpenCodeCli_node_exports);
|
|
24
|
+
var import_chat_models = require("@langchain/core/language_models/chat_models");
|
|
25
|
+
var import_messages = require("@langchain/core/messages");
|
|
26
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
27
|
+
var import_sharedFields = require("../../../utils/sharedFields");
|
|
28
|
+
var import_N8nLlmTracing = require("../N8nLlmTracing");
|
|
29
|
+
var import_child_process = require("child_process");
|
|
30
|
+
var import_fs = require("fs");
|
|
31
|
+
const TOOL_CALL_SYSTEM_PROMPT = `You have access to the following tools. When you need to call a tool, respond ONLY with a JSON block in this exact format (no other text before or after):
|
|
32
|
+
|
|
33
|
+
\`\`\`tool_calls
|
|
34
|
+
[{"id": "call_1", "name": "tool_name", "args": {"param": "value"}}]
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
When you do NOT need to call a tool, respond normally with text. Never mix tool calls and text in the same response.
|
|
38
|
+
|
|
39
|
+
Available tools:
|
|
40
|
+
`;
|
|
41
|
+
class ChatOpenCodeCLI extends import_chat_models.BaseChatModel {
|
|
42
|
+
constructor(fields) {
|
|
43
|
+
super({});
|
|
44
|
+
this.boundTools = [];
|
|
45
|
+
this.model = fields.model;
|
|
46
|
+
this.binaryPath = fields.binaryPath;
|
|
47
|
+
this.workingDirectory = fields.workingDirectory;
|
|
48
|
+
}
|
|
49
|
+
_llmType() {
|
|
50
|
+
return "opencode-cli";
|
|
51
|
+
}
|
|
52
|
+
bindTools(tools, kwargs) {
|
|
53
|
+
console.log("[LmChatOpenCodeCli] bindTools called, tool count:", tools.length);
|
|
54
|
+
const clone = new ChatOpenCodeCLI({
|
|
55
|
+
model: this.model,
|
|
56
|
+
binaryPath: this.binaryPath,
|
|
57
|
+
workingDirectory: this.workingDirectory
|
|
58
|
+
});
|
|
59
|
+
clone.boundTools = tools;
|
|
60
|
+
clone.callbacks = this.callbacks;
|
|
61
|
+
if (kwargs) {
|
|
62
|
+
return clone.bind(kwargs);
|
|
63
|
+
}
|
|
64
|
+
return clone;
|
|
65
|
+
}
|
|
66
|
+
async _generate(messages, _options, _runManager) {
|
|
67
|
+
console.log("[LmChatOpenCodeCli] _generate called", {
|
|
68
|
+
messageCount: messages.length,
|
|
69
|
+
boundToolCount: this.boundTools.length,
|
|
70
|
+
model: this.model
|
|
71
|
+
});
|
|
72
|
+
const processedMessages = [...messages];
|
|
73
|
+
if (this.boundTools.length > 0) {
|
|
74
|
+
const toolDescriptions = this.boundTools.map((tool) => {
|
|
75
|
+
const t = tool;
|
|
76
|
+
const name = t.name ?? "";
|
|
77
|
+
const description = t.description ?? "";
|
|
78
|
+
const schema = t.parameters ?? t.schema ?? {};
|
|
79
|
+
return `- ${name}: ${description}
|
|
80
|
+
Parameters: ${JSON.stringify(schema)}`;
|
|
81
|
+
}).join("\n\n");
|
|
82
|
+
const systemPrompt = TOOL_CALL_SYSTEM_PROMPT + toolDescriptions;
|
|
83
|
+
processedMessages.unshift(new import_messages.SystemMessage(systemPrompt));
|
|
84
|
+
console.log(
|
|
85
|
+
"[LmChatOpenCodeCli] injected tool system prompt, tool count:",
|
|
86
|
+
this.boundTools.length
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const prompt = processedMessages.map((m) => {
|
|
90
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
91
|
+
if (m instanceof import_messages.SystemMessage) return `[system]: ${content}`;
|
|
92
|
+
if (m instanceof import_messages.HumanMessage) return `[user]: ${content}`;
|
|
93
|
+
if (m instanceof import_messages.AIMessage) return `[assistant]: ${content}`;
|
|
94
|
+
return `[${m._getType()}]: ${content}`;
|
|
95
|
+
}).join("\n\n");
|
|
96
|
+
console.log("[LmChatOpenCodeCli] prompt built, length:", prompt.length);
|
|
97
|
+
const rawResponse = await this.executeOpenCodeCli(prompt);
|
|
98
|
+
console.log("[LmChatOpenCodeCli] raw response received, length:", rawResponse.length);
|
|
99
|
+
if (this.boundTools.length > 0) {
|
|
100
|
+
const toolCalls = this.extractToolCalls(rawResponse);
|
|
101
|
+
if (toolCalls.length > 0) {
|
|
102
|
+
console.log("[LmChatOpenCodeCli] extracted tool calls:", toolCalls.length);
|
|
103
|
+
const aiMessage2 = new import_messages.AIMessage({
|
|
104
|
+
content: "",
|
|
105
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
106
|
+
id: tc.id,
|
|
107
|
+
name: tc.name,
|
|
108
|
+
args: tc.args,
|
|
109
|
+
type: "tool_call"
|
|
110
|
+
}))
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
generations: [{ message: aiMessage2, text: "" }]
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
console.log("[LmChatOpenCodeCli] returning text response");
|
|
118
|
+
const aiMessage = new import_messages.AIMessage({ content: rawResponse });
|
|
119
|
+
return {
|
|
120
|
+
generations: [{ message: aiMessage, text: rawResponse }]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
extractToolCalls(text) {
|
|
124
|
+
const toolCallRegex = /```tool_calls\s*\n([\s\S]*?)\n```/;
|
|
125
|
+
const match = toolCallRegex.exec(text);
|
|
126
|
+
if (!match) return [];
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(match[1]);
|
|
129
|
+
if (!Array.isArray(parsed)) return [];
|
|
130
|
+
console.log("[LmChatOpenCodeCli] parsed tool calls from response:", parsed.length);
|
|
131
|
+
return parsed.map((tc, i) => ({
|
|
132
|
+
id: tc.id ?? `call_${i}`,
|
|
133
|
+
name: tc.name,
|
|
134
|
+
args: tc.args ?? {}
|
|
135
|
+
}));
|
|
136
|
+
} catch {
|
|
137
|
+
console.log("[LmChatOpenCodeCli] failed to parse tool calls JSON block");
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async executeOpenCodeCli(prompt) {
|
|
142
|
+
const args = ["run", "--format", "json"];
|
|
143
|
+
if (this.model && this.model !== "auto") {
|
|
144
|
+
args.push("--model", this.model);
|
|
145
|
+
}
|
|
146
|
+
args.push(prompt);
|
|
147
|
+
const cwd = this.workingDirectory?.trim() || void 0;
|
|
148
|
+
console.log("[LmChatOpenCodeCli] spawning opencode run", {
|
|
149
|
+
binaryPath: this.binaryPath,
|
|
150
|
+
args: args.map((a, i) => i === args.length - 1 ? `<prompt len=${a.length}>` : a),
|
|
151
|
+
model: this.model,
|
|
152
|
+
cwd
|
|
153
|
+
});
|
|
154
|
+
return await new Promise((resolve, reject) => {
|
|
155
|
+
const child = (0, import_child_process.spawn)(this.binaryPath, args, {
|
|
156
|
+
cwd,
|
|
157
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
158
|
+
env: { ...process.env }
|
|
159
|
+
});
|
|
160
|
+
let stdout = "";
|
|
161
|
+
let stderr = "";
|
|
162
|
+
child.stdout.on("data", (data) => {
|
|
163
|
+
stdout += data.toString();
|
|
164
|
+
});
|
|
165
|
+
child.stderr.on("data", (data) => {
|
|
166
|
+
stderr += data.toString();
|
|
167
|
+
});
|
|
168
|
+
child.on("error", (err) => {
|
|
169
|
+
console.error("[LmChatOpenCodeCli] spawn error:", err.message);
|
|
170
|
+
reject(
|
|
171
|
+
new Error(
|
|
172
|
+
`Failed to spawn opencode: ${err.message}. Make sure OpenCode CLI is installed (brew install opencode-ai/tap/opencode or curl -fsSL https://opencode.ai/install | bash) and accessible. Working directory: ${cwd ?? "<default>"}`
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
child.on("close", (code) => {
|
|
177
|
+
console.log("[LmChatOpenCodeCli] opencode run exited", {
|
|
178
|
+
code,
|
|
179
|
+
stdoutLength: stdout.length,
|
|
180
|
+
stderrLength: stderr.length
|
|
181
|
+
});
|
|
182
|
+
const parseResult = this.parseJsonEventOutput(stdout);
|
|
183
|
+
if (parseResult.assistantText) {
|
|
184
|
+
console.log(
|
|
185
|
+
"[LmChatOpenCodeCli] parsed assistant content, length:",
|
|
186
|
+
parseResult.assistantText.length
|
|
187
|
+
);
|
|
188
|
+
resolve(parseResult.assistantText);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (parseResult.errorMessage) {
|
|
192
|
+
console.error("[LmChatOpenCodeCli] opencode returned error:", parseResult.errorMessage);
|
|
193
|
+
reject(new Error(`OpenCode CLI error: ${parseResult.errorMessage}`));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (code !== 0) {
|
|
197
|
+
const stderrMsg = stderr.trim();
|
|
198
|
+
const errorMsg = stderrMsg || `opencode run exited with code ${code}`;
|
|
199
|
+
console.error("[LmChatOpenCodeCli] opencode run failed with code", code, ":", errorMsg);
|
|
200
|
+
reject(new Error(errorMsg));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
console.error(
|
|
204
|
+
"[LmChatOpenCodeCli] no assistant response parsed from output, stdout preview:",
|
|
205
|
+
stdout.substring(0, 500)
|
|
206
|
+
);
|
|
207
|
+
reject(new Error("No assistant response received from opencode run"));
|
|
208
|
+
});
|
|
209
|
+
if (child.stdin) {
|
|
210
|
+
child.stdin.end();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Parse JSON event output from `opencode run --format json`.
|
|
216
|
+
*
|
|
217
|
+
* Actual event types from opencode run --format json (verified empirically):
|
|
218
|
+
* - {"type":"step_start","part":{"type":"step-start",...}}
|
|
219
|
+
* - {"type":"text","part":{"type":"text","text":"...the response...",...}}
|
|
220
|
+
* - {"type":"step_finish","part":{"type":"step-finish","reason":"stop","cost":...,"tokens":{...}}}
|
|
221
|
+
*
|
|
222
|
+
* Returns both assistant text and any error messages found.
|
|
223
|
+
*/
|
|
224
|
+
parseJsonEventOutput(output) {
|
|
225
|
+
const lines = output.split("\n").filter((line) => line.trim());
|
|
226
|
+
const assistantParts = [];
|
|
227
|
+
const errorParts = [];
|
|
228
|
+
console.log("[LmChatOpenCodeCli] parsing JSON event output, line count:", lines.length);
|
|
229
|
+
for (const line of lines) {
|
|
230
|
+
try {
|
|
231
|
+
const parsed = JSON.parse(line);
|
|
232
|
+
const eventType = parsed.type;
|
|
233
|
+
console.log(
|
|
234
|
+
"[LmChatOpenCodeCli] JSON event:",
|
|
235
|
+
eventType,
|
|
236
|
+
"| keys:",
|
|
237
|
+
Object.keys(parsed).join(",")
|
|
238
|
+
);
|
|
239
|
+
if (eventType === "text") {
|
|
240
|
+
const part = parsed.part;
|
|
241
|
+
if (part?.type === "text" && typeof part.text === "string") {
|
|
242
|
+
console.log(
|
|
243
|
+
"[LmChatOpenCodeCli] found text event, text length:",
|
|
244
|
+
part.text.length
|
|
245
|
+
);
|
|
246
|
+
assistantParts.push(part.text);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (eventType === "message") {
|
|
250
|
+
const part = parsed.part;
|
|
251
|
+
if (part && typeof part.text === "string") {
|
|
252
|
+
assistantParts.push(part.text);
|
|
253
|
+
}
|
|
254
|
+
if (parsed.role === "assistant" && typeof parsed.content === "string") {
|
|
255
|
+
assistantParts.push(parsed.content);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (eventType === "assistant") {
|
|
259
|
+
const message = parsed.message;
|
|
260
|
+
if (message?.content) {
|
|
261
|
+
if (Array.isArray(message.content)) {
|
|
262
|
+
for (const c of message.content) {
|
|
263
|
+
if (c.type === "text" && typeof c.text === "string") {
|
|
264
|
+
assistantParts.push(c.text);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} else if (typeof message.content === "string") {
|
|
268
|
+
assistantParts.push(message.content);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (eventType === "error") {
|
|
273
|
+
const errMsg = typeof parsed.message === "string" ? parsed.message : typeof parsed.error === "string" ? parsed.error : void 0;
|
|
274
|
+
if (errMsg) {
|
|
275
|
+
console.error("[LmChatOpenCodeCli] error event received:", errMsg);
|
|
276
|
+
errorParts.push(errMsg);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (eventType === "step_finish") {
|
|
280
|
+
const part = parsed.part;
|
|
281
|
+
if (part) {
|
|
282
|
+
console.log(
|
|
283
|
+
"[LmChatOpenCodeCli] step_finish event, reason:",
|
|
284
|
+
part.reason,
|
|
285
|
+
"cost:",
|
|
286
|
+
part.cost
|
|
287
|
+
);
|
|
288
|
+
if (part.reason === "error" && typeof part.error === "string") {
|
|
289
|
+
errorParts.push(part.error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
assistantText: assistantParts.join(""),
|
|
298
|
+
errorMessage: errorParts.join("; ")
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
class LmChatOpenCodeCli {
|
|
303
|
+
constructor() {
|
|
304
|
+
this.description = {
|
|
305
|
+
displayName: "OpenCode CLI Chat Model",
|
|
306
|
+
name: "lmChatOpenCodeCli",
|
|
307
|
+
icon: "file:openCodeCli.svg",
|
|
308
|
+
group: ["transform"],
|
|
309
|
+
version: [1],
|
|
310
|
+
description: "Chat model powered by the OpenCode CLI. Requires opencode to be installed locally (brew install opencode-ai/tap/opencode or curl -fsSL https://opencode.ai/install | bash).",
|
|
311
|
+
defaults: {
|
|
312
|
+
name: "OpenCode CLI Chat Model"
|
|
313
|
+
},
|
|
314
|
+
codex: {
|
|
315
|
+
categories: ["AI"],
|
|
316
|
+
subcategories: {
|
|
317
|
+
AI: ["Language Models", "Root Nodes"],
|
|
318
|
+
"Language Models": ["Chat Models (Recommended)"]
|
|
319
|
+
},
|
|
320
|
+
resources: {}
|
|
321
|
+
},
|
|
322
|
+
inputs: [],
|
|
323
|
+
outputs: [import_n8n_workflow.NodeConnectionTypes.AiLanguageModel],
|
|
324
|
+
outputNames: ["Model"],
|
|
325
|
+
properties: [
|
|
326
|
+
(0, import_sharedFields.getConnectionHintNoticeField)([import_n8n_workflow.NodeConnectionTypes.AiChain, import_n8n_workflow.NodeConnectionTypes.AiAgent]),
|
|
327
|
+
{
|
|
328
|
+
displayName: "Model",
|
|
329
|
+
name: "model",
|
|
330
|
+
type: "options",
|
|
331
|
+
description: 'The model to use via opencode CLI. Format: provider/model (e.g. anthropic/claude-sonnet-4-20250514). Select "Auto" to use the default model configured in opencode.',
|
|
332
|
+
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
|
|
333
|
+
options: [
|
|
334
|
+
{ name: "Auto (Default)", value: "auto" },
|
|
335
|
+
// OpenCode built-in models
|
|
336
|
+
{ name: "OpenCode Big Pickle", value: "opencode/big-pickle" },
|
|
337
|
+
{ name: "OpenCode GPT-5 Nano", value: "opencode/gpt-5-nano" },
|
|
338
|
+
{ name: "OpenCode Hy3 Preview Free", value: "opencode/hy3-preview-free" },
|
|
339
|
+
// Anthropic models
|
|
340
|
+
{ name: "Claude Sonnet 4 (Anthropic)", value: "anthropic/claude-sonnet-4-20250514" },
|
|
341
|
+
{ name: "Claude Opus 4 (Anthropic)", value: "anthropic/claude-opus-4-20250918" },
|
|
342
|
+
{
|
|
343
|
+
name: "Claude 3.5 Sonnet (Anthropic)",
|
|
344
|
+
value: "anthropic/claude-3-5-sonnet-20241022"
|
|
345
|
+
},
|
|
346
|
+
// OpenAI models
|
|
347
|
+
{ name: "GPT-4o (OpenAI)", value: "openai/gpt-4o" },
|
|
348
|
+
{ name: "GPT-4o Mini (OpenAI)", value: "openai/gpt-4o-mini" },
|
|
349
|
+
{ name: "o3 (OpenAI)", value: "openai/o3" },
|
|
350
|
+
{ name: "o3 Mini (OpenAI)", value: "openai/o3-mini" },
|
|
351
|
+
// Google models
|
|
352
|
+
{ name: "Gemini 2.5 Pro (Google)", value: "google/gemini-2.5-pro" },
|
|
353
|
+
{ name: "Gemini 2.5 Flash (Google)", value: "google/gemini-2.5-flash" },
|
|
354
|
+
{ name: "Gemini 2.0 Flash (Google)", value: "google/gemini-2.0-flash" },
|
|
355
|
+
// xAI models
|
|
356
|
+
{ name: "Grok 3 (xAI)", value: "xai/grok-3" },
|
|
357
|
+
{ name: "Grok 3 Mini (xAI)", value: "xai/grok-3-mini" },
|
|
358
|
+
// DeepSeek models
|
|
359
|
+
{ name: "DeepSeek Chat (DeepSeek)", value: "deepseek/deepseek-chat" },
|
|
360
|
+
{ name: "DeepSeek Reasoner (DeepSeek)", value: "deepseek/deepseek-reasoner" }
|
|
361
|
+
],
|
|
362
|
+
default: "auto"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
displayName: "Options",
|
|
366
|
+
name: "options",
|
|
367
|
+
placeholder: "Add Option",
|
|
368
|
+
description: "Additional options to configure",
|
|
369
|
+
type: "collection",
|
|
370
|
+
default: {},
|
|
371
|
+
options: [
|
|
372
|
+
{
|
|
373
|
+
displayName: "Binary Path",
|
|
374
|
+
name: "binaryPath",
|
|
375
|
+
default: "opencode",
|
|
376
|
+
description: 'Path to the opencode binary. Defaults to "opencode" (must be in PATH).',
|
|
377
|
+
type: "string"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
displayName: "Working Directory",
|
|
381
|
+
name: "workingDirectory",
|
|
382
|
+
default: "",
|
|
383
|
+
description: "Working directory for the opencode process. Leave empty to use the default.",
|
|
384
|
+
type: "string"
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
async supplyData(itemIndex) {
|
|
392
|
+
const modelName = this.getNodeParameter("model", itemIndex);
|
|
393
|
+
const binaryPath = this.getNodeParameter("options.binaryPath", itemIndex, "opencode");
|
|
394
|
+
const rawWorkingDirectory = this.getNodeParameter("options.workingDirectory", itemIndex, "", {
|
|
395
|
+
rawExpressions: true
|
|
396
|
+
});
|
|
397
|
+
const workingDirectory = this.getNodeParameter("options.workingDirectory", itemIndex, "");
|
|
398
|
+
const normalizedWorkingDirectory = (workingDirectory ?? "").trim();
|
|
399
|
+
const rawWorkingDirectoryValue = rawWorkingDirectory ?? "";
|
|
400
|
+
const isWorkingDirectoryExpression = rawWorkingDirectoryValue.startsWith("=") || rawWorkingDirectoryValue.includes("{{") || rawWorkingDirectoryValue.includes("$workspace");
|
|
401
|
+
console.log("[LmChatOpenCodeCli] resolved OpenCode CLI options", {
|
|
402
|
+
itemIndex,
|
|
403
|
+
modelName,
|
|
404
|
+
binaryPath,
|
|
405
|
+
rawWorkingDirectory,
|
|
406
|
+
workingDirectory: normalizedWorkingDirectory
|
|
407
|
+
});
|
|
408
|
+
if (isWorkingDirectoryExpression && !normalizedWorkingDirectory) {
|
|
409
|
+
throw new import_n8n_workflow.ApplicationError(
|
|
410
|
+
`OpenCode CLI working directory expression resolved to an empty value: ${rawWorkingDirectoryValue}`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
if (normalizedWorkingDirectory.includes("{{") || normalizedWorkingDirectory.includes("$workspace")) {
|
|
414
|
+
throw new import_n8n_workflow.ApplicationError(
|
|
415
|
+
`OpenCode CLI working directory was not resolved before execution: ${normalizedWorkingDirectory}`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
if (normalizedWorkingDirectory && (!(0, import_fs.existsSync)(normalizedWorkingDirectory) || !(0, import_fs.statSync)(normalizedWorkingDirectory).isDirectory())) {
|
|
419
|
+
throw new import_n8n_workflow.ApplicationError(
|
|
420
|
+
`OpenCode CLI working directory does not exist or is not a directory: ${normalizedWorkingDirectory}`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
console.log("[LmChatOpenCodeCli] creating ChatOpenCodeCLI instance", {
|
|
424
|
+
model: modelName,
|
|
425
|
+
binaryPath,
|
|
426
|
+
workingDirectory: normalizedWorkingDirectory
|
|
427
|
+
});
|
|
428
|
+
const model = new ChatOpenCodeCLI({
|
|
429
|
+
model: modelName,
|
|
430
|
+
binaryPath,
|
|
431
|
+
workingDirectory: normalizedWorkingDirectory
|
|
432
|
+
});
|
|
433
|
+
model.callbacks = [new import_N8nLlmTracing.N8nLlmTracing(this)];
|
|
434
|
+
return {
|
|
435
|
+
response: model
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
440
|
+
0 && (module.exports = {
|
|
441
|
+
LmChatOpenCodeCli
|
|
442
|
+
});
|
|
443
|
+
//# sourceMappingURL=LmChatOpenCodeCli.node.js.map
|