DownloadPlugin streams the plugin binary to the client.
(req *pb.DownloadPluginRequest, stream pb.ChatCLIService_DownloadPluginServer)
| 228 | |
| 229 | // DownloadPlugin streams the plugin binary to the client. |
| 230 | func (h *Handler) DownloadPlugin(req *pb.DownloadPluginRequest, stream pb.ChatCLIService_DownloadPluginServer) error { |
| 231 | if h.pluginManager == nil { |
| 232 | return status.Errorf(codes.Unavailable, "%s", i18n.T("server.remote.plugin_unavailable")) |
| 233 | } |
| 234 | if req.PluginName == "" { |
| 235 | return status.Errorf(codes.InvalidArgument, "%s", i18n.T("server.remote.plugin_name_required")) |
| 236 | } |
| 237 | |
| 238 | plugin, ok := h.pluginManager.GetPlugin(req.PluginName) |
| 239 | if !ok { |
| 240 | return status.Errorf(codes.NotFound, "%s", i18n.T("server.remote.plugin_not_found", req.PluginName)) |
| 241 | } |
| 242 | |
| 243 | pluginPath := plugin.Path() |
| 244 | info, err := os.Stat(pluginPath) |
| 245 | if err != nil { |
| 246 | return status.Errorf(codes.Internal, "%s", i18n.T("server.remote.plugin_stat_error", err)) |
| 247 | } |
| 248 | |
| 249 | f, err := os.Open(pluginPath) //#nosec G304 -- path supplied by user/agent through validated tool surface (boundary check upstream) |
| 250 | if err != nil { |
| 251 | return status.Errorf(codes.Internal, "%s", i18n.T("server.remote.plugin_open_error", err)) |
| 252 | } |
| 253 | defer f.Close() |
| 254 | |
| 255 | const chunkSize = 64 * 1024 // 64KB chunks |
| 256 | buf := make([]byte, chunkSize) |
| 257 | filename := info.Name() |
| 258 | totalSize := info.Size() |
| 259 | first := true |
| 260 | |
| 261 | for { |
| 262 | n, readErr := f.Read(buf) |
| 263 | if n > 0 { |
| 264 | resp := &pb.DownloadPluginResponse{ |
| 265 | Chunk: buf[:n], |
| 266 | Done: false, |
| 267 | } |
| 268 | if first { |
| 269 | resp.Filename = filename |
| 270 | resp.TotalSize = totalSize |
| 271 | first = false |
| 272 | } |
| 273 | if err := stream.Send(resp); err != nil { |
| 274 | return err |
| 275 | } |
| 276 | } |
| 277 | if readErr == io.EOF { |
| 278 | break |
| 279 | } |
| 280 | if readErr != nil { |
| 281 | return status.Errorf(codes.Internal, "%s", i18n.T("server.remote.plugin_read_error", readErr)) |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | // Send final empty chunk with done=true |
| 286 | return stream.Send(&pb.DownloadPluginResponse{Done: true}) |
| 287 | } |