Files
kandev/apps/backend/internal/plugins/invoke.go
T
Kandev Agent c53f699714 feat: add plugin system with grpc runtime and native ui plugins
- go plugin backends ship as release tarballs (per-platform binaries, checksums) that
  kandev installs from url/upload or filesystem sideload+sync, then spawns and
  supervises via hashicorp/go-plugin (unix socket / loopback tcp+automtls)
- strict typed grpc contract (kandev.plugin.v1): DeliverEvent/InvokeTool/HandleWebhook
  plus a capability-gated host service (state, secrets, emit); author sdk in
  pkg/pluginsdk; packaging cli in cmd/plugin-pack
- native frontend bundles: window.registerKandevPlugin + registry/host loader with
  dynamic routes, nav items, slots and ws handlers; plugins settings page with
  install dialog, sync button and enable/disable/uninstall
- gated behind the plugins feature flag (prod off, dev/e2e on); covered by unit and
  integration tests plus playwright e2e against a real packaged fixture plugin

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:55:45 +01:00

49 lines
1.6 KiB
Go

package plugins
import (
"context"
"fmt"
"time"
"github.com/kandev/kandev/pkg/pluginsdk"
)
// DefaultToolTimeout bounds a single agent tool invocation
// (Service.InvokeTool), per docs/specs/plugins/spec.md ("Agent tool
// invocation").
const DefaultToolTimeout = 30 * time.Second
// InvokeTool routes a tool call to id's live subprocess via the runtime
// manager's RemotePlugin.InvokeTool RPC, applying DefaultToolTimeout on top
// of ctx. Returns an error if id has no live process (not active, or
// mid-restart).
func (s *Service) InvokeTool(ctx context.Context, id string, req *pluginsdk.ToolRequest) (*pluginsdk.ToolResponse, error) {
remote, ok := s.pluginRemote(id)
if !ok {
return nil, fmt.Errorf("plugins: plugin %q is not running", id)
}
reqCtx, cancel := context.WithTimeout(ctx, DefaultToolTimeout)
defer cancel()
return remote.InvokeTool(reqCtx, req)
}
// InvokeWebhook routes an inbound webhook to id's live subprocess via the
// runtime manager's RemotePlugin.HandleWebhook RPC. Used by
// POST/GET /api/plugins/:id/webhooks/:key.
func (s *Service) InvokeWebhook(ctx context.Context, id string, req *pluginsdk.WebhookRequest) (*pluginsdk.WebhookResponse, error) {
remote, ok := s.pluginRemote(id)
if !ok {
return nil, fmt.Errorf("plugins: plugin %q is not running", id)
}
return remote.HandleWebhook(ctx, req)
}
// pluginRemote returns the live RemotePlugin for id, if the runtime manager
// is wired and currently tracking a running process for it.
func (s *Service) pluginRemote(id string) (*pluginsdk.RemotePlugin, bool) {
if s.runtime == nil {
return nil, false
}
return s.runtime.Get(id)
}