Path: blob/main/src/vs/platform/agentHost/common/agentHostClientResourceChannel.ts
13394 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { decodeBase64, encodeBase64, VSBuffer } from '../../../base/common/buffer.js';6import { Event } from '../../../base/common/event.js';7import { URI } from '../../../base/common/uri.js';8import { IChannel, IServerChannel } from '../../../base/parts/ipc/common/ipc.js';9import { IFileService } from '../../files/common/files.js';10import { IRemoteFilesystemConnection } from './agentHostFileSystemProvider.js';11import {12ContentEncoding, type DirectoryEntry, type ResourceDeleteParams, type ResourceDeleteResult,13type ResourceListResult, type ResourceMoveParams, type ResourceMoveResult,14type ResourceReadResult, type ResourceWriteParams, type ResourceWriteResult,15} from './state/protocol/commands.js';1617/**18* IPC channel name used for in-process agent-host → renderer reverse19* filesystem RPCs. The renderer registers a server channel under this20* name on its `MessagePortClient`; the agent host reaches it via21* `connection.channelClient.getChannel(name)` on its `UtilityProcessServer`.22*23* Mirrors the WebSocket reverse-RPC handlers in24* {@link RemoteAgentHostProtocolClient._handleReverseRequest}.25*/26export const AGENT_HOST_CLIENT_RESOURCE_CHANNEL = 'agentHostClientResource';2728/**29* Server-side channel implementation handling resource RPCs from the30* agent host. Backed by the local {@link IFileService} on the renderer.31*/32export class AgentHostClientResourceChannel implements IServerChannel {3334constructor(private readonly _fileService: IFileService) { }3536listen<T>(_ctx: unknown, event: string): Event<T> {37throw new Error(`No event '${event}' on AgentHostClientResourceChannel`);38}3940async call<T>(_ctx: unknown, command: string, arg?: unknown): Promise<T> {41const a = (arg ?? {}) as Record<string, unknown>;42switch (command) {43case 'resourceList': {44const stat = await this._fileService.resolve(URI.parse(a.uri as string));45if (!stat.isDirectory) {46throw new Error(`Resource is not a directory: ${a.uri}`);47}48const entries: DirectoryEntry[] = (stat.children ?? []).map(c => ({49name: c.name,50type: c.isDirectory ? 'directory' : 'file',51}));52const result: ResourceListResult = { entries };53return result as T;54}55case 'resourceRead': {56const content = await this._fileService.readFile(URI.parse(a.uri as string));57const result: ResourceReadResult = {58data: encodeBase64(content.value),59encoding: ContentEncoding.Base64,60};61return result as T;62}63case 'resourceWrite': {64const params = a as unknown as ResourceWriteParams & { createOnly?: boolean };65const writeUri = URI.parse(params.uri as unknown as string);66const buf = params.encoding === ContentEncoding.Base6467? decodeBase64(params.data)68: VSBuffer.fromString(params.data);69if (params.createOnly) {70await this._fileService.createFile(writeUri, buf, { overwrite: false });71} else {72await this._fileService.writeFile(writeUri, buf);73}74const result: ResourceWriteResult = {};75return result as T;76}77case 'resourceDelete': {78const params = a as unknown as ResourceDeleteParams;79await this._fileService.del(URI.parse(params.uri as unknown as string), { recursive: !!params.recursive });80const result: ResourceDeleteResult = {};81return result as T;82}83case 'resourceMove': {84const params = a as unknown as ResourceMoveParams;85await this._fileService.move(URI.parse(params.source as unknown as string), URI.parse(params.destination as unknown as string), !params.failIfExists);86const result: ResourceMoveResult = {};87return result as T;88}89}90throw new Error(`Unknown command '${command}' on AgentHostClientResourceChannel`);91}92}9394/**95* Wraps an {@link IChannel} (typically obtained from the agent host's96* `UtilityProcessServer.getChannel`) into an97* {@link IRemoteFilesystemConnection} suitable for registering with98* {@link AgentHostClientFileSystemProvider.registerAuthority}.99*/100export function createAgentHostClientResourceConnection(channel: IChannel): IRemoteFilesystemConnection {101return {102resourceList: (uri) => channel.call('resourceList', { uri: uri.toString() }) as Promise<ResourceListResult>,103resourceRead: (uri) => channel.call('resourceRead', { uri: uri.toString() }) as Promise<ResourceReadResult>,104resourceWrite: (params) => channel.call('resourceWrite', { ...params, uri: params.uri.toString() }) as Promise<ResourceWriteResult>,105resourceDelete: (params) => channel.call('resourceDelete', { ...params, uri: params.uri.toString() }) as Promise<ResourceDeleteResult>,106resourceMove: (params) => channel.call('resourceMove', { ...params, source: params.source.toString(), destination: params.destination.toString() }) as Promise<ResourceMoveResult>,107};108}109110111