mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-22 19:14:33 -07:00
test
This commit is contained in:
@@ -97,6 +97,7 @@ import { isAlternateBufferEnabled } from './ui/hooks/useAlternateBuffer.js';
|
||||
import { setupTerminalAndTheme } from './utils/terminalTheme.js';
|
||||
import { profiler } from './ui/components/DebugProfiler.js';
|
||||
import { runDeferredCommand } from './deferred.js';
|
||||
import { remoteInputServer } from './utils/remoteInputServer.js';
|
||||
|
||||
const SLOW_RENDER_MS = 200;
|
||||
|
||||
@@ -206,6 +207,9 @@ export async function startInteractiveUI(
|
||||
consolePatcher.patch();
|
||||
registerCleanup(consolePatcher.cleanup);
|
||||
|
||||
remoteInputServer.start();
|
||||
registerCleanup(() => remoteInputServer.stop());
|
||||
|
||||
const { stdout: inkStdout, stderr: inkStderr } = createWorkingStdio();
|
||||
|
||||
// Create wrapper component to use hooks inside render
|
||||
|
||||
@@ -993,6 +993,16 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleRemoteInput = (text: string) => {
|
||||
handleFinalSubmit(text);
|
||||
};
|
||||
appEvents.on(AppEvent.RemoteInput, handleRemoteInput);
|
||||
return () => {
|
||||
appEvents.off(AppEvent.RemoteInput, handleRemoteInput);
|
||||
};
|
||||
}, [handleFinalSubmit]);
|
||||
|
||||
const handleClearScreen = useCallback(() => {
|
||||
historyManager.clearItems();
|
||||
clearConsoleMessagesState();
|
||||
|
||||
@@ -11,6 +11,7 @@ export enum AppEvent {
|
||||
Flicker = 'flicker',
|
||||
SelectionWarning = 'selection-warning',
|
||||
PasteTimeout = 'paste-timeout',
|
||||
RemoteInput = 'remote-input',
|
||||
}
|
||||
|
||||
export interface AppEvents {
|
||||
@@ -18,6 +19,7 @@ export interface AppEvents {
|
||||
[AppEvent.Flicker]: never[];
|
||||
[AppEvent.SelectionWarning]: never[];
|
||||
[AppEvent.PasteTimeout]: never[];
|
||||
[AppEvent.RemoteInput]: string[];
|
||||
}
|
||||
|
||||
export const appEvents = new EventEmitter<AppEvents>();
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import http from 'node:http';
|
||||
import { appEvents, AppEvent } from './events.js';
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
|
||||
const REMOTE_INPUT_PORT = 41243;
|
||||
|
||||
export class RemoteInputServer {
|
||||
private server: http.Server | undefined;
|
||||
|
||||
start() {
|
||||
this.server = http.createServer((req, res) => {
|
||||
// Enable CORS
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'POST' && (req.url === '/message' || req.url === '/message/stream')) {
|
||||
let body = '';
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk.toString();
|
||||
});
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const data = JSON.parse(body);
|
||||
// Handle both raw format { text: "..." } and A2A format
|
||||
let text = '';
|
||||
|
||||
// 1. Check for A2A JSON-RPC format
|
||||
if (data.params?.message?.parts?.[0]?.text) {
|
||||
text = data.params.message.parts[0].text;
|
||||
}
|
||||
// 2. Check for simple format
|
||||
else if (data.text) {
|
||||
text = data.text;
|
||||
}
|
||||
// 3. Check for message object (A2A style params body)
|
||||
else if (data.message?.parts?.[0]?.text) {
|
||||
text = data.message.parts[0].text;
|
||||
}
|
||||
// 4. Check for message object (standard content style)
|
||||
else if (data.message?.content?.[0]?.text) {
|
||||
text = data.message.content[0].text;
|
||||
}
|
||||
|
||||
if (text) {
|
||||
debugLogger.log('[RemoteInputServer] Received input:', text);
|
||||
appEvents.emit(AppEvent.RemoteInput, text);
|
||||
|
||||
// Respond with SSE-like structure or just OK
|
||||
// Chrome Extension expects SSE if it hits /message/stream
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive'
|
||||
});
|
||||
res.write(`data: ${JSON.stringify({
|
||||
result: {
|
||||
kind: 'status-update',
|
||||
status: {
|
||||
message: {
|
||||
parts: [{ kind: 'text', text: 'Input received by interactive CLI.' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
})}\n\n`);
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'No text found in body' }));
|
||||
}
|
||||
} catch (e) {
|
||||
debugLogger.error('[RemoteInputServer] Error parsing body:', e);
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
}
|
||||
});
|
||||
|
||||
this.server.listen(REMOTE_INPUT_PORT, '127.0.0.1', () => {
|
||||
debugLogger.log(`[RemoteInputServer] Listening on http://127.0.0.1:${REMOTE_INPUT_PORT}`);
|
||||
});
|
||||
|
||||
this.server.on('error', (err) => {
|
||||
debugLogger.warn(`[RemoteInputServer] Failed to start: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
this.server = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const remoteInputServer = new RemoteInputServer();
|
||||
Reference in New Issue
Block a user