Files
sub2api/backend/pkg/pluginapi/v1/runtime.go
T
shaw 29eead917f feat(pluginapi): HostService 账号目录返回结构化只读元数据
纯宿主侧改动,向后兼容。

- 契约(pkg/pluginapi/v1/plugin.proto,已重新生成):ListAccounts 增补
  repeated AccountInfo accounts(保留 account_ids 向后兼容),新增 metadata_json
  只读快照字段。HostServiceAPIVersion 1→2(InitHostServices 运行时协商,加法式,
  旧调用方忽略新字段即可)。
- 权限边界:可见范围由清单能力经 pluginCapabilityAccountScopeGrants 推导,
  ListAccounts 与 ResolveOutboundIdentity 均以该范围为权威边界。
- metadata_json 采用 denylist:剥离 Credentials 与含反向引用的关联对象
  Groups/AccountGroups(避免 encoding/json 循环引用),其余字段自动透传,
  数据模型新增非机密字段无需改宿主。

测试:范围/元数据/无凭据泄漏用例、denylist 反射护栏 + 循环引用安全断言、纵深过滤用例;
build/vet/gofmt、-tags unit 与 -race 全绿。
2026-09-20 11:26:19 +08:00

87 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package pluginv1
import (
"context"
hcplugin "github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
const (
// ProtocolVersion 是宿主与插件进程握手协议版本。
ProtocolVersion = 1
// TransportAPIVersion 是 OpenAI OAuth 出站传输契约版本。
TransportAPIVersion = 1
// UIBridgeVersion 是插件管理页与沙箱 UI 的消息协议版本。
UIBridgeVersion = 1
// HostServiceAPIVersion 是宿主反向服务(HostService)的契约版本。它独立于
// TransportAPIVersion:宿主服务是叠加在传输契约之上的可选能力,通过
// InitHostServices 在运行时协商,因此新增宿主能力不会使既有插件失效。
//
// v2:ListAccounts 在 account_ids 之外增补 AccountInfo 全量可读元数据
// (状态/调度/限流/暂停原因等),供插件排除不可调度账号。纯加法变更,v1
// 插件忽略新字段即可继续工作。
HostServiceAPIVersion = 2
// TransportPluginName 是 go-plugin 中注册的唯一能力名称。
TransportPluginName = "oauth_transport"
)
// HandshakeConfig 防止普通可执行文件被误当成 Sub2API 插件启动。
var HandshakeConfig = hcplugin.HandshakeConfig{
ProtocolVersion: ProtocolVersion,
MagicCookieKey: "SUB2API_PLUGIN_MAGIC_COOKIE",
MagicCookieValue: "sub2api-plugin-v1",
}
// HostBrokerReceiver 由需要反向调用宿主服务的插件传输实现选择性实现。
// GRPCServer 在插件进程启动注册服务时把 go-plugin broker 交给它,插件随后可在
// InitHostServices 中用宿主下发的 broker id 拨号回宿主。宿主侧不实现此接口。
type HostBrokerReceiver interface {
SetHostBroker(broker *hcplugin.GRPCBroker)
}
// TransportClient 是宿主 Dispense 传输能力后拿到的句柄,除 gRPC 客户端外还捆绑了
// 本连接的 go-plugin broker,宿主据此把 HostService 反向暴露给插件。
type TransportClient struct {
TransportPluginClient
Broker *hcplugin.GRPCBroker
}
// GRPCPlugin 把生成的 gRPC 服务注册到 go-plugin 子进程。
type GRPCPlugin struct {
hcplugin.NetRPCUnsupportedPlugin
Impl TransportPluginServer
}
func (p *GRPCPlugin) GRPCServer(broker *hcplugin.GRPCBroker, server *grpc.Server) error {
RegisterTransportPluginServer(server, p.Impl)
// 插件侧:把 broker 交给需要反向访问宿主服务的实现(可选能力)。
if receiver, ok := p.Impl.(HostBrokerReceiver); ok {
receiver.SetHostBroker(broker)
}
return nil
}
func (p *GRPCPlugin) GRPCClient(_ context.Context, broker *hcplugin.GRPCBroker, conn *grpc.ClientConn) (any, error) {
// 宿主侧:捆绑 broker,供 startPluginRuntime 反向暴露 HostService。
return &TransportClient{TransportPluginClient: NewTransportPluginClient(conn), Broker: broker}, nil
}
// ClientPluginMap 返回宿主侧使用的插件声明。
func ClientPluginMap() map[string]hcplugin.Plugin {
return map[string]hcplugin.Plugin{
TransportPluginName: &GRPCPlugin{},
}
}
// Serve 启动一个实现了传输协议的插件进程。
func Serve(impl TransportPluginServer) {
hcplugin.Serve(&hcplugin.ServeConfig{
HandshakeConfig: HandshakeConfig,
Plugins: map[string]hcplugin.Plugin{
TransportPluginName: &GRPCPlugin{Impl: impl},
},
GRPCServer: hcplugin.DefaultGRPCServer,
})
}