chrome-devtools-frontend
Version:
Chrome DevTools UI
118 lines (103 loc) • 3.62 kB
text/typescript
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export class GreenDevAgentGeminiCliSocketClient {
#websocket: WebSocket;
#sessionId?: string;
#activeMessage = '';
#messageLog: string[] = [];
#promptResolve: ((response: string) => void)|null = null;
sessionReady: Promise<void>;
#sessionReadyResolve: ((value: void|PromiseLike<void>) => void)|null = null;
constructor() {
this.sessionReady = new Promise(resolve => {
this.#sessionReadyResolve = resolve;
});
this.#websocket = new WebSocket('ws://localhost:6655');
this.#websocket.onopen = this.#onOpen.bind(this);
this.#websocket.onmessage = this.#onMessage.bind(this);
this.#websocket.onclose = this.#onClose.bind(this);
this.#websocket.onerror = this.#onError.bind(this);
}
#onOpen(): void {
console.warn('WebSocket connected.');
this.#websocket.send(
JSON.stringify({jsonrpc: '2.0', method: 'session/new', params: {cwd: '.', mcpServers: []}, id: 14}));
}
#onMessage(event: MessageEvent): void {
this.#messageLog.push(event.data);
try {
const data = JSON.parse(event.data);
if (data?.result?.sessionId && !this.#sessionId) {
this.#sessionId = data.result.sessionId;
console.warn(`Successfully created new session with ID: ${this.#sessionId}`);
if (this.#sessionReadyResolve) {
this.#sessionReadyResolve();
this.#sessionReadyResolve = null;
}
}
const update = data?.params?.update;
if (update?.sessionUpdate === 'agent_message_chunk') {
this.#activeMessage += update.content?.text || '';
}
if (data?.result?.stopReason) {
if (this.#activeMessage) {
console.warn(this.#activeMessage);
}
console.warn(`Stop Reason: ${data.result.stopReason}`, this.#messageLog);
if (this.#promptResolve) {
this.#promptResolve(this.#activeMessage);
this.#promptResolve = null;
}
this.#activeMessage = '';
this.#messageLog = [];
} else if (data?.method === 'session/request_permission') {
// TODO(finnur): Needs augmenting once I turn off YOLO in the bridge.
this.#websocket.send(JSON.stringify({
jsonrpc: '2.0',
id: data.id,
result: {
outcome: {
selected: {
optionId: 'proceed_always',
},
},
},
}));
}
} catch (e) {
// Log the raw data if it's not valid JSON.
console.error('Failed to parse WebSocket message or process data:', event.data, e);
}
}
#onClose(): void {
console.warn('WebSocket disconnected.');
}
#onError(error: Event): void {
console.error('WebSocket error:', error);
}
sendPrompt(promptText: string): Promise<string> {
return new Promise((resolve, reject) => {
if (this.#promptResolve) {
reject(new Error('Another prompt is already in progress.'));
return;
}
if (!this.#sessionId) {
reject(new Error('Cannot send prompt without a session ID.'));
return;
}
this.#promptResolve = resolve;
console.warn(`Sending prompt: "${promptText}"`);
console.warn('Thinking...');
this.#websocket.send(JSON.stringify({
jsonrpc: '2.0',
method: 'session/prompt',
params: {
prompt: [{type: 'text', text: promptText}],
sessionId: this.#sessionId,
},
id: 15,
}));
});
}
}