Add directory listing to protocol, and folder picker for remote agent hosts (#301639)

* Add directory listing to protocol, and folder picker for remote agent hosts

* Protocol improvements

* Don't register agenthost URIs as workspace folders

* Update pickers after merge

* Resolve comments
This commit is contained in:
Rob Lourens
2026-03-14 10:26:39 -07:00
committed by GitHub
parent 10d159c89d
commit ce5e503640
18 changed files with 582 additions and 47 deletions

View File

@@ -61,6 +61,8 @@ class MockProtocolServer implements IProtocolServer {
class MockSideEffectHandler implements IProtocolSideEffectHandler {
readonly handledActions: ISessionAction[] = [];
readonly browsedUris: URI[] = [];
handleAction(action: ISessionAction): void {
this.handledActions.push(action);
}
@@ -68,6 +70,18 @@ class MockSideEffectHandler implements IProtocolSideEffectHandler {
handleDisposeSession(_session: URI): void { }
async handleListSessions(): Promise<ISessionSummary[]> { return []; }
handleSetAuthToken(_token: string): void { }
async handleBrowseDirectory(uri: URI): Promise<{ entries: { name: string; type: 'file' | 'directory' }[] }> {
this.browsedUris.push(uri);
return {
entries: [
{ name: 'src', type: 'directory' },
{ name: 'README.md', type: 'file' },
],
};
}
getDefaultDirectory(): URI {
return URI.file('/home/testuser');
}
}
// ---- Helpers ----------------------------------------------------------------
@@ -80,10 +94,6 @@ function request(id: number, method: string, params?: unknown): IProtocolMessage
return { jsonrpc: '2.0', id, method, params } as IProtocolMessage;
}
function findNotification(sent: IProtocolMessage[], method: string): IProtocolNotification | undefined {
return sent.find(isJsonRpcNotification) as IProtocolNotification | undefined;
}
function findNotifications(sent: IProtocolMessage[], method: string): IProtocolNotification[] {
return sent.filter(isJsonRpcNotification) as IProtocolNotification[];
}
@@ -314,4 +324,35 @@ suite('ProtocolServerHandler', () => {
assert.strictEqual(transport.sent.length, 0);
});
test('handshake includes defaultDirectory from side effects', () => {
const transport = connectClient('client-home');
const resp = findResponse(transport.sent, 1);
assert.ok(resp);
const result = (resp as { result: IInitializeResult }).result;
assert.strictEqual(URI.revive(result.defaultDirectory!).fsPath, '/home/testuser');
});
test('browseDirectory routes to side effect handler', async () => {
const transport = connectClient('client-browse');
transport.sent.length = 0;
const dirUri = URI.file('/home/user/project');
transport.simulateMessage(request(2, 'browseDirectory', { uri: dirUri }));
await new Promise(resolve => setTimeout(resolve, 10));
assert.strictEqual(sideEffects.browsedUris.length, 1);
assert.strictEqual(sideEffects.browsedUris[0].fsPath, '/home/user/project');
const resp = findResponse(transport.sent, 2);
assert.ok(resp);
const result = (resp as { result: { entries: { name: string; uri: unknown; type: string }[] } }).result;
assert.strictEqual(result.entries.length, 2);
assert.strictEqual(result.entries[0].name, 'src');
assert.strictEqual(result.entries[0].type, 'directory');
assert.strictEqual(result.entries[1].name, 'README.md');
assert.strictEqual(result.entries[1].type, 'file');
});
});