Skip to content

Add /remote-control command to Gemini CLI#1

Open
braddux wants to merge 1 commit intomainfrom
remote-control-feature-17076370056261385580
Open

Add /remote-control command to Gemini CLI#1
braddux wants to merge 1 commit intomainfrom
remote-control-feature-17076370056261385580

Conversation

@braddux
Copy link
Copy Markdown
Owner

@braddux braddux commented Feb 26, 2026

Added /remote-control command to Gemini CLI.
Implemented RemoteControlService to handle WebSocket communication.
Added remoteControlEmitter to support injecting input into the main CLI loop.
Updated AppContainer to listen for remote input.
Registered the new command in BuiltinCommandLoader.
Added unit tests for the command.


PR created automatically by Jules for task 17076370056261385580 started by @braddux

This commit introduces a new `/remote-control` slash command that allows controlling the Gemini CLI session remotely via a WebSocket connection.

Features:
- `RemoteControlService`: Handles WebSocket connection, command execution, and output streaming.
- `remoteControlCommand`: UI command to start the session, generate a session token, and display connection info.
- `remoteControlEmitter`: Mechanism to inject remote input into the main CLI loop (e.g., for chat interactions).
- Integration with `ShellExecutionService` for running shell commands.
- Secure token generation for session validation.
- Integration with `AppContainer` to handle remote input injection.

The feature is designed to connect to a backend service (defaulting to `ws://localhost:8080` but configurable via `GEMINI_REMOTE_CONTROL_URL`) to bridge the local CLI with a remote interface (e.g., Google Chat).

Co-authored-by: braddux <959674+braddux@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @braddux, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant new capability to the Gemini CLI: remote control. By adding a /remote-control command, users can now establish a WebSocket connection to an external service, enabling remote execution of commands, input injection, and real-time forwarding of CLI output and logs. This enhancement allows for programmatic interaction with the CLI, potentially integrating it with other systems like Google Chat for automated workflows or remote management.

Highlights

  • New Command Added: Introduced a new /remote-control command to the Gemini CLI, enabling remote interaction capabilities.
  • Remote Control Service: Implemented RemoteControlService to establish and manage WebSocket connections, facilitating remote command execution, input handling, and output forwarding.
  • Input Injection Mechanism: Added remoteControlEmitter to allow external systems to inject input directly into the main CLI loop.
  • CLI Integration: Modified AppContainer to listen for and process commands received via the new remote control input emitter.
  • Command Registration: Registered the new remoteControlCommand within the BuiltinCommandLoader for discoverability and execution.
  • Unit Tests: Included comprehensive unit tests to ensure the correct functionality of the remote control command.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/cli/src/services/BuiltinCommandLoader.ts
    • Imported remoteControlCommand.
    • Registered remoteControlCommand in the list of built-in commands.
  • packages/cli/src/services/RemoteControlService.ts
    • Added RemoteControlService class to manage WebSocket connections.
    • Implemented methods for connecting, disconnecting, and handling incoming WebSocket messages (exec, stdin, resize, input).
    • Included logic to subscribe to and forward CoreEvent.Output and CoreEvent.ConsoleLog events to the remote client.
    • Provided functionality to execute shell commands and write to PTYs based on remote instructions.
  • packages/cli/src/services/remoteControlEmitter.ts
    • Added remoteControlEmitter (an EventEmitter instance) and REMOTE_INPUT_EVENT constant.
    • Defined RemoteInputPayload interface for type safety.
  • packages/cli/src/ui/AppContainer.tsx
    • Imported remoteControlEmitter and RemoteInputPayload.
    • Added a useEffect hook to subscribe to REMOTE_INPUT_EVENT and call handleFinalSubmit with the received input.
  • packages/cli/src/ui/commands/remoteControlCommand.test.ts
    • Added unit tests for the remoteControlCommand, verifying its ability to connect to the RemoteControlService with a generated token.
  • packages/cli/src/ui/commands/remoteControlCommand.ts
    • Added remoteControlCommand definition, including its name, description, and action.
    • Implemented the action to initialize RemoteControlService, generate a session token, connect to a WebSocket URL (from GEMINI_REMOTE_CONTROL_URL or default), and display connection status.
Activity
  • The pull request was automatically created by Jules for task 17076370056261385580 started by @braddux.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a powerful /remote-control command, enabling remote operation of the CLI via WebSockets. However, the implementation has critical security vulnerabilities, including Remote Code Execution (RCE), session hijacking, and environment variable exposure, stemming from a lack of strict validation, sanitization, and authorization for remote inputs. The RemoteControlService and AppContainer require updates to address these issues. Additionally, there are opportunities to improve resource management and align the new tests with the repository's conventions.

Comment on lines +113 to +117
case 'exec':
if (message.command) {
this.executeCommand(message.command);
}
break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The RemoteControlService directly executes commands received from the WebSocket server without any validation or sanitization. This allows a malicious or compromised server to achieve Remote Code Execution (RCE) on the user's machine by sending an exec command.

Tracing the data flow:

  1. handleMessage (line 109) receives raw data from the WebSocket.
  2. JSON.parse (line 111) extracts the command property.
  3. executeCommand (line 115) is called with the untrusted command.
  4. ShellExecutionService.execute (line 150) runs the command in a shell.

This is a critical security vulnerability. You must implement a strict allow-list of permitted commands or ensure that the remote server is fully trusted and the connection is securely authenticated.

Comment on lines +165 to +169
sanitizationConfig: {
allowedEnvironmentVariables: [],
blockedEnvironmentVariables: [],
enableEnvironmentVariableRedaction: false,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The sanitizationConfig for ShellExecutionService.execute is configured to be overly permissive. It allows remotely executed commands to access all environment variables of the Gemini CLI process. This is a significant security risk, as it could expose sensitive information like API keys, authentication tokens, and other secrets stored in the environment.

For a remote execution feature, the environment should be heavily restricted by default. You should block all environment variables and only allow specific, safe ones if necessary.

Suggested change
sanitizationConfig: {
allowedEnvironmentVariables: [],
blockedEnvironmentVariables: [],
enableEnvironmentVariableRedaction: false,
},
sanitizationConfig: {
allowedEnvironmentVariables: [], // Or a very small, safe subset
blockedEnvironmentVariables: ['*'], // Block all by default
enableEnvironmentVariableRedaction: true,
},

Comment on lines +118 to +122
case 'stdin':
if (message.pid && message.input) {
ShellExecutionService.writeToPty(message.pid, message.input);
}
break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The RemoteControlService allows the remote WebSocket server to inject arbitrary input into any active PTY process via the stdin message type. This can be used to hijack interactive sessions or execute commands in an already open shell.

Tracing the data flow:

  1. handleMessage (line 109) receives raw data.
  2. ShellExecutionService.writeToPty (line 120) is called with the untrusted message.input and message.pid.

This should be restricted to only allow input to processes explicitly managed by the remote session, and only after proper authorization.

Comment on lines +1114 to +1122
useEffect(() => {
const handleRemoteInput = (payload: RemoteInputPayload) => {
handleFinalSubmit(payload.input);
};
remoteControlEmitter.on(REMOTE_INPUT_EVENT, handleRemoteInput);
return () => {
remoteControlEmitter.off(REMOTE_INPUT_EVENT, handleRemoteInput);
};
}, [handleFinalSubmit]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The AppContainer component listens for remote input events and passes them directly to handleFinalSubmit. This allows a remote attacker (via the WebSocket server) to execute any CLI slash command (e.g., /quit, /settings) or submit arbitrary prompts to the LLM.

Tracing the data flow:

  1. handleRemoteInput (line 1115) receives the payload from remoteControlEmitter.
  2. handleFinalSubmit (line 1116) processes the input as if it came from the local user.

This effectively grants full control over the CLI session to the remote server. Remote input should be strictly validated and restricted to a safe subset of operations.

Comment on lines +25 to +39
const originalEnv = process.env;

beforeEach(() => {
vi.clearAllMocks();
process.env = { ...originalEnv };
mockContext = createMockCommandContext({
ui: {
addItem: vi.fn(),
},
} as any);
});

afterEach(() => {
process.env = originalEnv;
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This test modifies process.env directly, which violates the repository's testing conventions. The style guide (lines 67-71) requires using vi.stubEnv() and vi.unstubAllEnvs() to manage environment variables in tests. This prevents test leakage and is more reliable.

Suggested change
const originalEnv = process.env;
beforeEach(() => {
vi.clearAllMocks();
process.env = { ...originalEnv };
mockContext = createMockCommandContext({
ui: {
addItem: vi.fn(),
},
} as any);
});
afterEach(() => {
process.env = originalEnv;
});
beforeEach(() => {
vi.clearAllMocks();
mockContext = createMockCommandContext({
ui: {
addItem: vi.fn(),
},
} as any);
});
afterEach(() => {
vi.unstubAllEnvs();
});
References
  1. When testing code that depends on environment variables, use vi.stubEnv('NAME', 'value') in beforeEach and vi.unstubAllEnvs() in afterEach. Avoid modifying process.env directly as it can lead to test leakage and is less reliable. (link)

});

it('should start a remote control session', async () => {
process.env['GEMINI_REMOTE_CONTROL_URL'] = 'ws://test-url';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In accordance with the repository's testing conventions for environment variables (lines 67-71), please use vi.stubEnv() to set this environment variable for the test.

Suggested change
process.env['GEMINI_REMOTE_CONTROL_URL'] = 'ws://test-url';
vi.stubEnv('GEMINI_REMOTE_CONTROL_URL', 'ws://test-url');
References
  1. When testing code that depends on environment variables, use vi.stubEnv('NAME', 'value') in beforeEach and vi.unstubAllEnvs() in afterEach. Avoid modifying process.env directly as it can lead to test leakage and is less reliable. (link)

service = new RemoteControlService(url);

try {
await service.connect();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

After a successful connection, a cleanup function should be registered to ensure service.disconnect() is called when the CLI exits. This prevents ungraceful connection termination and ensures resources are properly released. You'll need to import registerCleanup from ../../utils/cleanup.js.

      await service.connect();
      registerCleanup(() => service?.disconnect());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant