From 4ebc38a8e018eb2238086910600349c7d61a01c8 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 29 Jan 2024 09:35:34 -0800 Subject: [PATCH] Allow agent to be debugged by IntelliJ If you set some new env vars you can spawn the agent with --inspect, which allows IntelliJ to connect to it. There is also an option to have the agent listen on a socket for the client to attach. --- .gitignore | 1 + agent/src/agent.ts | 2 +- agent/src/cli/jsonrpc.ts | 51 +++++++++++++++++++++++++---------- vscode/src/chat/protocol.ts | 2 +- vscode/src/jsonrpc/jsonrpc.ts | 2 +- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 4fa4b43995a..a4bec3de8ea 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ dist/ .DS_Store .env .idea/ +.run/ playwright/**/*.webm **/*.iml **/*.vsix diff --git a/agent/src/agent.ts b/agent/src/agent.ts index f3dc2f2e0de..d0fda94a5bb 100644 --- a/agent/src/agent.ts +++ b/agent/src/agent.ts @@ -142,7 +142,7 @@ export class Agent extends MessageHandler { public workspace = new AgentWorkspaceDocuments({ edit: (uri, callback, options) => { if (this.clientInfo?.capabilities?.edit !== 'enabled') { - logDebug('CodyAgent', 'client does not support operation: AgenTextDocument.edit()') + logDebug('CodyAgent', 'client does not support operation: textDocument/edit') return Promise.resolve(false) } const edits: TextEdit[] = [] diff --git a/agent/src/cli/jsonrpc.ts b/agent/src/cli/jsonrpc.ts index 6fa08183c11..551e1ccaf76 100644 --- a/agent/src/cli/jsonrpc.ts +++ b/agent/src/cli/jsonrpc.ts @@ -1,6 +1,7 @@ import type { EXPIRY_STRATEGY, MODE, Polly, Request } from '@pollyjs/core' import * as commander from 'commander' import { Command, Option } from 'commander' +import { createServer } from 'net' import { startPollyRecording } from '../../../vscode/src/testutils/polly' import { Agent } from '../agent' @@ -44,6 +45,11 @@ function expiryStrategyOption(value: string): EXPIRY_STRATEGY { } } +const isDebugMode = process.env.CODY_AGENT_DEBUG_REMOTE === 'true' +const debugPort = process.env.CODY_AGENT_DEBUG_PORT + ? parseInt(process.env.CODY_AGENT_DEBUG_PORT, 10) + : 3113 + export const jsonrpcCommand = new Command('jsonrpc') .description( 'Interact with the Agent using JSON-RPC via stdout/stdin. ' + @@ -132,21 +138,38 @@ export const jsonrpcCommand = new Command('jsonrpc') process.exit(1) } - if (process.env.CODY_DEBUG === 'true') { - process.stderr.write('Starting Cody Agent...\n') + if (isDebugMode) { + const server = createServer(socket => { + setupAgentCommunication(polly, networkRequests, socket, socket) + }) + + server.listen(debugPort, () => { + console.log(`Agent debug server listening on port ${debugPort}`) + }) + } else { + setupAgentCommunication(polly, networkRequests, process.stdin, process.stdout) } + }) - const agent = new Agent({ polly, networkRequests }) +function setupAgentCommunication( + polly: Polly | undefined, + networkRequests: Request[], + stdin: NodeJS.ReadableStream, + stdout: NodeJS.WritableStream +) { + const agent = new Agent({ polly, networkRequests }) - // Force the agent process to exit when stdin/stdout close as an attempt to - // prevent zombie agent processes. We experienced this problem when we - // forcefully exit the IntelliJ process during local `./gradlew :runIde` - // workflows. We manually confirmed that this logic makes the agent exit even - // when we forcefully quit IntelliJ - // https://github.com/sourcegraph/cody/pull/1439#discussion_r1365610354 - process.stdout.on('close', () => process.exit(1)) - process.stdin.on('close', () => process.exit(1)) + // Force the agent process to exit when stdin/stdout close as an attempt to + // prevent zombie agent processes. We experienced this problem when we + // forcefully exit the IntelliJ process during local `./gradlew :runIde` + // workflows. We manually confirmed that this logic makes the agent exit even + // when we forcefully quit IntelliJ + // https://github.com/sourcegraph/cody/pull/1439#discussion_r1365610354 + if (!isDebugMode) { + stdout.on('close', () => process.exit(1)) + stdin.on('close', () => process.exit(1)) + } - process.stdin.pipe(agent.messageDecoder) - agent.messageEncoder.pipe(process.stdout) - }) + stdin.pipe(agent.messageDecoder) + agent.messageEncoder.pipe(stdout) +} diff --git a/vscode/src/chat/protocol.ts b/vscode/src/chat/protocol.ts index c08330d5e82..12fa877f3d0 100644 --- a/vscode/src/chat/protocol.ts +++ b/vscode/src/chat/protocol.ts @@ -50,7 +50,7 @@ export type WebviewMessage = command: 'openLocalFileWithRange' filePath: string // Note: we're not using vscode.Range objects or nesting here, as the protocol - // tends ot munge the type in a weird way (nested fields become array indices). + // tends to munge the type in a weird way (nested fields become array indices). range?: { startLine: number startCharacter: number diff --git a/vscode/src/jsonrpc/jsonrpc.ts b/vscode/src/jsonrpc/jsonrpc.ts index bb7a5fbb21d..f516040fd1d 100644 --- a/vscode/src/jsonrpc/jsonrpc.ts +++ b/vscode/src/jsonrpc/jsonrpc.ts @@ -105,7 +105,7 @@ type MessageHandlerCallback = (err: Error | null, msg: Message | null) => void /** * Absolute path to a file where the agent can write low-level debugging logs to - * trace all incoming/outgoin JSON messages. + * trace all incoming/outgoing JSON messages. */ const tracePath = process.env.CODY_AGENT_TRACE_PATH ?? ''