Merge pull request 'Host options for compiled plugins' (#201) from feature/svc-plugin-host-options into main
ci / vet-and-test (push) Successful in 58s
ci / vet-and-test (push) Successful in 58s
Why: compiled plugins need host options and a stable surface version.
Evidence: Actions run 3286 job 5051 vet-and-test success on 80bb123c87.
Impact: surface 2.14.0-experimental; process-event child page no longer assumes an exact two-row window.
Refs: #201
This commit was merged in pull request #201.
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
|||||||
"agent-system-backend/internal/app"
|
"agent-system-backend/internal/app"
|
||||||
"agent-system-backend/internal/config"
|
"agent-system-backend/internal/config"
|
||||||
"agent-system-backend/internal/store"
|
"agent-system-backend/internal/store"
|
||||||
|
"agent-system-backend/pkg/extension"
|
||||||
"agent-system-backend/pkg/identity"
|
"agent-system-backend/pkg/identity"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,7 +74,19 @@ func main() {
|
|||||||
pgIdleSec := fs.Int("pg-conn-max-idle-sec", 0, "PG 空闲连接回收秒数(0=默认 300)")
|
pgIdleSec := fs.Int("pg-conn-max-idle-sec", 0, "PG 空闲连接回收秒数(0=默认 300)")
|
||||||
pgLifetimeSec := fs.Int("pg-conn-max-lifetime-sec", 0, "PG 连接最长生命周期秒数(0=不限)")
|
pgLifetimeSec := fs.Int("pg-conn-max-lifetime-sec", 0, "PG 连接最长生命周期秒数(0=不限)")
|
||||||
pgQueryTimeoutSec := fs.Int("pg-query-timeout-sec", 0, "PG statement_timeout 秒数(0=不限;覆盖全部业务查询)")
|
pgQueryTimeoutSec := fs.Int("pg-query-timeout-sec", 0, "PG statement_timeout 秒数(0=不限;覆盖全部业务查询)")
|
||||||
|
extension.RegisterCLIFlags(fs)
|
||||||
fs.Parse(os.Args[1:])
|
fs.Parse(os.Args[1:])
|
||||||
|
handled, hookErr := extension.RunCLIHooks(extension.Options{
|
||||||
|
Addr: *addr, DataDir: *data, PluginsDir: *pluginsDir, Mock: *mock,
|
||||||
|
StorageDSN: *dsn, AuthMode: *auth, DisableExecTools: *noExec, ExecBackend: *execBackend,
|
||||||
|
})
|
||||||
|
if hookErr != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "agent-system:", hookErr)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if handled {
|
||||||
|
return
|
||||||
|
}
|
||||||
cliSet := visited(fs)
|
cliSet := visited(fs)
|
||||||
|
|
||||||
resolved, err := config.Load(*cfgFile, os.LookupEnv, config.Config{
|
resolved, err := config.Load(*cfgFile, os.LookupEnv, config.Config{
|
||||||
@@ -94,11 +107,36 @@ func main() {
|
|||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
pluginDefaults, merr := extension.MergeHostOptions(extension.Options{})
|
||||||
|
if merr != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "agent-system:", merr)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
disableExec := resolved.DisableExecTools
|
||||||
|
if !cliSet["disableExecTools"] {
|
||||||
|
disableExec = disableExec || pluginDefaults.DisableExecTools
|
||||||
|
}
|
||||||
|
execBackendResolved := resolved.ExecBackend
|
||||||
|
if !cliSet["execBackend"] && execBackendResolved == "" {
|
||||||
|
execBackendResolved = pluginDefaults.ExecBackend
|
||||||
|
}
|
||||||
|
extOpt := extension.Options{
|
||||||
|
Addr: resolved.Addr, DataDir: resolved.DataDir, PluginsDir: resolved.PluginsDir,
|
||||||
|
DisableUploadIndexing: pluginDefaults.DisableUploadIndexing,
|
||||||
|
DisableConvCardMirror: pluginDefaults.DisableConvCardMirror,
|
||||||
|
DisableExecTools: disableExec, ExecBackend: execBackendResolved,
|
||||||
|
}
|
||||||
|
if err := extension.PrepareCompiled(&extOpt); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "agent-system:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
if err := app.Run(app.Options{
|
if err := app.Run(app.Options{
|
||||||
Addr: resolved.Addr, DataDir: resolved.DataDir, PluginsDir: resolved.PluginsDir, ForceMock: resolved.Mock,
|
Addr: resolved.Addr, DataDir: resolved.DataDir, PluginsDir: resolved.PluginsDir, ForceMock: resolved.Mock,
|
||||||
StorageDSN: resolved.StorageDSN, ArtifactsDir: resolved.ArtifactsDir, AuthMode: resolved.AuthMode,
|
StorageDSN: resolved.StorageDSN, ArtifactsDir: resolved.ArtifactsDir, AuthMode: resolved.AuthMode,
|
||||||
InstanceID: resolved.Instance, InsecureListen: resolved.InsecureListen, AllowedHosts: resolved.AllowedHosts(),
|
InstanceID: resolved.Instance, InsecureListen: resolved.InsecureListen, AllowedHosts: resolved.AllowedHosts(),
|
||||||
DisableExecTools: resolved.DisableExecTools, ExecBackend: resolved.ExecBackend, ExecDepsDir: resolved.ExecDepsDir,
|
DisableUploadIndexing: extOpt.DisableUploadIndexing,
|
||||||
|
DisableConvCardMirror: extOpt.DisableConvCardMirror,
|
||||||
|
DisableExecTools: extOpt.DisableExecTools, ExecBackend: extOpt.ExecBackend, ExecDepsDir: resolved.ExecDepsDir,
|
||||||
ExecWorkspace: resolved.ExecWorkspace,
|
ExecWorkspace: resolved.ExecWorkspace,
|
||||||
Tier: resolved.Tier,
|
Tier: resolved.Tier,
|
||||||
PGPool: store.PoolOptions{
|
PGPool: store.PoolOptions{
|
||||||
|
|||||||
@@ -70,8 +70,24 @@ func TestRunProcessEventsAreOrderedAndBoundToConversation(t *testing.T) {
|
|||||||
t.Fatalf("child page: %d %v", code, childPage)
|
t.Fatalf("child page: %d %v", code, childPage)
|
||||||
}
|
}
|
||||||
childRows := childPage["events"].([]any)
|
childRows := childPage["events"].([]any)
|
||||||
if len(childRows) != 2 || childRows[0].(map[string]any)["type"] != string(core.EvAgentProgress) || childRows[1].(map[string]any)["type"] != string(core.EvAgentResult) {
|
var childTypes []string
|
||||||
t.Fatalf("child progress/result not retained or raw child output leaked: %v", childRows)
|
for _, row := range childRows {
|
||||||
|
ev := row.(map[string]any)
|
||||||
|
typ, _ := ev["type"].(string)
|
||||||
|
if typ == string(core.EvToolOutput) {
|
||||||
|
payload, _ := ev["payload"].(map[string]any)
|
||||||
|
if payload["text"] == "child raw output" {
|
||||||
|
t.Fatalf("raw child output leaked: %v", childRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ == string(core.EvAgentProgress) || typ == string(core.EvAgentResult) {
|
||||||
|
childTypes = append(childTypes, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 父运行的终态事件可能在 waitRunTerminal 之后才落库,序号会落在这次追加的
|
||||||
|
// 工具输出和子运行事件之间。这里只要求子运行进度与结果按序保留。
|
||||||
|
if len(childTypes) != 2 || childTypes[0] != string(core.EvAgentProgress) || childTypes[1] != string(core.EvAgentResult) {
|
||||||
|
t.Fatalf("child progress/result not retained: types=%v rows=%v", childTypes, childRows)
|
||||||
}
|
}
|
||||||
code, _ = doJSON(t, h, http.MethodGet, "/api/conversations/"+otherConvID+"/runs/"+runID+"/events", nil)
|
code, _ = doJSON(t, h, http.MethodGet, "/api/conversations/"+otherConvID+"/runs/"+runID+"/events", nil)
|
||||||
if code != http.StatusNotFound {
|
if code != http.StatusNotFound {
|
||||||
|
|||||||
@@ -2045,10 +2045,15 @@ func (s *Server) listAdminZoneSections(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// registry 统一注册表视图(来源标记 + 按来源启停入口)。
|
// registry 统一注册表视图(来源标记 + 按来源启停入口)。
|
||||||
func (s *Server) registry(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) registry(w http.ResponseWriter, r *http.Request) {
|
||||||
|
compiledIDs := make([]string, 0, len(s.CompiledPlugins))
|
||||||
|
for _, cp := range s.CompiledPlugins {
|
||||||
|
compiledIDs = append(compiledIDs, cp.ID)
|
||||||
|
}
|
||||||
reg := map[string]any{
|
reg := map[string]any{
|
||||||
"tools": s.visibleTools(s.owner(r)),
|
"tools": s.visibleTools(s.owner(r)),
|
||||||
"parsers": ingest.ParsersDescription(),
|
"parsers": ingest.ParsersDescription(),
|
||||||
"panels": flattenPanels(s.plugins.List()),
|
"panels": flattenPanels(s.plugins.List()),
|
||||||
|
"compiled": compiledIDs,
|
||||||
}
|
}
|
||||||
writeJSON(w, http.StatusOK, reg)
|
writeJSON(w, http.StatusOK, reg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
core "agent-system-core"
|
core "agent-system-core"
|
||||||
@@ -36,7 +38,9 @@ import (
|
|||||||
// ResourceAuthzQuery.Source for per-document publication authorization.
|
// ResourceAuthzQuery.Source for per-document publication authorization.
|
||||||
// 2.13.0-experimental: optional AccountDirectoryProvider and trusted in-process
|
// 2.13.0-experimental: optional AccountDirectoryProvider and trusted in-process
|
||||||
// AccountDirectoryRegistrar for scoped, current account candidate decisions.
|
// AccountDirectoryRegistrar for scoped, current account candidate decisions.
|
||||||
const SurfaceVersion = "2.13.0-experimental"
|
// 2.14.0-experimental:InProc.HostOptions / CLIHook,以及 MergeHostOptions、
|
||||||
|
// RegisterCLIFlags、RunCLIHooks(组合二进制把插件声明并进基座启动,不覆盖显式参数)。
|
||||||
|
const SurfaceVersion = "2.14.0-experimental"
|
||||||
|
|
||||||
// ErrNotLinked 表示进程没有空白导入 link 包,Main 无法启动服务。
|
// ErrNotLinked 表示进程没有空白导入 link 包,Main 无法启动服务。
|
||||||
var ErrNotLinked = errors.New("extension: runner not linked; blank-import agent-system-backend/pkg/extension/link")
|
var ErrNotLinked = errors.New("extension: runner not linked; blank-import agent-system-backend/pkg/extension/link")
|
||||||
@@ -164,10 +168,24 @@ type IntentSpec struct {
|
|||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CLIHook 让编译插件在基座 serve 旗标集上登记自己的旗标,并在解析后决定是否接管本次进程。
|
||||||
|
// 插件只登记自己的旗标,不重复基座已有旗标。基座在 flag.Parse 前调用 Register,解析后调用 AfterParse。
|
||||||
|
type CLIHook struct {
|
||||||
|
Register func(fs *flag.FlagSet)
|
||||||
|
AfterParse func(opt Options) (handled bool, err error)
|
||||||
|
}
|
||||||
|
|
||||||
// InProc 是随下游二进制编译进来的扩展。
|
// InProc 是随下游二进制编译进来的扩展。
|
||||||
type InProc struct {
|
type InProc struct {
|
||||||
ID string
|
ID string
|
||||||
Register func(reg Registry) error
|
Register func(reg Registry) error
|
||||||
|
// HostOptions 可选。组合二进制把它当作启动默认值:布尔开关按“任一为 true 则为 true”
|
||||||
|
// 合并,且不覆盖调用方显式传入的参数。未设置则无产品默认。
|
||||||
|
HostOptions func() Options
|
||||||
|
// CLI 可选。组合二进制在基座 serve 旗标上挂接插件专属旗标(如 bootstrap-admin)。
|
||||||
|
CLI *CLIHook
|
||||||
|
// Prepare 可选。基座合并 HostOptions 之后、进入服务之前调用,可改写 opt 或拒绝启动。
|
||||||
|
Prepare func(opt *Options) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options 是下游自有二进制的启动参数,字段与 agent-system serve 对齐。
|
// Options 是下游自有二进制的启动参数,字段与 agent-system serve 对齐。
|
||||||
@@ -203,6 +221,80 @@ var (
|
|||||||
runner func(Options) error
|
runner func(Options) error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MergeHostOptions 把已登记插件的 HostOptions 合并进 base。
|
||||||
|
// 布尔开关按“任一为 true 则为 true”。ExecBackend 取首个非空值,彼此冲突则返回错误。
|
||||||
|
// Addr、DataDir、PluginsDir、StorageDSN、AuthMode 等运行参数不从插件合并,避免覆盖调用方。
|
||||||
|
func MergeHostOptions(base Options) (Options, error) {
|
||||||
|
for _, e := range Compiled() {
|
||||||
|
if e.HostOptions == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opt := e.HostOptions()
|
||||||
|
if opt.DisableUploadIndexing {
|
||||||
|
base.DisableUploadIndexing = true
|
||||||
|
}
|
||||||
|
if opt.DisableConvCardMirror {
|
||||||
|
base.DisableConvCardMirror = true
|
||||||
|
}
|
||||||
|
if opt.DisableExecTools {
|
||||||
|
base.DisableExecTools = true
|
||||||
|
}
|
||||||
|
if opt.ExecBackend == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if base.ExecBackend != "" && base.ExecBackend != opt.ExecBackend {
|
||||||
|
return base, fmt.Errorf("extension: plugin %s exec backend %q conflicts with %q", e.ID, opt.ExecBackend, base.ExecBackend)
|
||||||
|
}
|
||||||
|
if base.ExecBackend == "" {
|
||||||
|
base.ExecBackend = opt.ExecBackend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCLIFlags 让每个已登记插件往 fs 登记自己的旗标。须在 Parse 之前调用。
|
||||||
|
func RegisterCLIFlags(fs *flag.FlagSet) {
|
||||||
|
if fs == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, e := range Compiled() {
|
||||||
|
if e.CLI != nil && e.CLI.Register != nil {
|
||||||
|
e.CLI.Register(fs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunCLIHooks 在旗标解析之后依次调用插件 AfterParse。
|
||||||
|
// 第一个 handled 的插件接管本次进程,基座不应再进入服务循环。
|
||||||
|
func RunCLIHooks(opt Options) (bool, error) {
|
||||||
|
for _, e := range Compiled() {
|
||||||
|
if e.CLI == nil || e.CLI.AfterParse == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
handled, err := e.CLI.AfterParse(opt)
|
||||||
|
if err != nil || handled {
|
||||||
|
return handled, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareCompiled 在 HostOptions 合并进 opt 之后调用各插件 Prepare。任一错误拒绝启动。
|
||||||
|
func PrepareCompiled(opt *Options) error {
|
||||||
|
if opt == nil {
|
||||||
|
return errors.New("extension: nil options")
|
||||||
|
}
|
||||||
|
for _, e := range Compiled() {
|
||||||
|
if e.Prepare == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := e.Prepare(opt); err != nil {
|
||||||
|
return fmt.Errorf("extension: %s: %w", e.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterCompiled 在 init 中登记进程内扩展。
|
// RegisterCompiled 在 init 中登记进程内扩展。
|
||||||
func RegisterCompiled(e InProc) {
|
func RegisterCompiled(e InProc) {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMergeHostOptionsORAndExecConflict(t *testing.T) {
|
||||||
|
ResetCompiled()
|
||||||
|
t.Cleanup(ResetCompiled)
|
||||||
|
RegisterCompiled(InProc{
|
||||||
|
ID: "a",
|
||||||
|
HostOptions: func() Options {
|
||||||
|
return Options{DisableUploadIndexing: true, ExecBackend: "sandbox"}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
RegisterCompiled(InProc{
|
||||||
|
ID: "b",
|
||||||
|
HostOptions: func() Options {
|
||||||
|
return Options{DisableExecTools: true, DisableConvCardMirror: true}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
got, err := MergeHostOptions(Options{Addr: "127.0.0.1:1"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !got.DisableUploadIndexing || !got.DisableExecTools || !got.DisableConvCardMirror {
|
||||||
|
t.Fatalf("bool merge: %+v", got)
|
||||||
|
}
|
||||||
|
if got.ExecBackend != "sandbox" || got.Addr != "127.0.0.1:1" {
|
||||||
|
t.Fatalf("kept caller addr and first exec backend: %+v", got)
|
||||||
|
}
|
||||||
|
RegisterCompiled(InProc{
|
||||||
|
ID: "c",
|
||||||
|
HostOptions: func() Options { return Options{ExecBackend: "direct"} },
|
||||||
|
})
|
||||||
|
if _, err := MergeHostOptions(Options{}); err == nil {
|
||||||
|
t.Fatal("expected exec backend conflict")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareCompiledCanRejectStartup(t *testing.T) {
|
||||||
|
ResetCompiled()
|
||||||
|
t.Cleanup(ResetCompiled)
|
||||||
|
RegisterCompiled(InProc{
|
||||||
|
ID: "rdm",
|
||||||
|
Prepare: func(opt *Options) error {
|
||||||
|
if opt.ExecBackend == "sandbox" {
|
||||||
|
return errors.New("no bwrap")
|
||||||
|
}
|
||||||
|
opt.DisableExecTools = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
opt := Options{ExecBackend: "sandbox"}
|
||||||
|
if err := PrepareCompiled(&opt); err == nil {
|
||||||
|
t.Fatal("expected prepare error")
|
||||||
|
}
|
||||||
|
opt.ExecBackend = "direct"
|
||||||
|
if err := PrepareCompiled(&opt); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !opt.DisableExecTools {
|
||||||
|
t.Fatal("prepare should be able to rewrite options")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCLIHookHandlesBeforeServe(t *testing.T) {
|
||||||
|
ResetCompiled()
|
||||||
|
t.Cleanup(ResetCompiled)
|
||||||
|
var admin *string
|
||||||
|
RegisterCompiled(InProc{
|
||||||
|
ID: "rdm",
|
||||||
|
CLI: &CLIHook{
|
||||||
|
Register: func(fs *flag.FlagSet) {
|
||||||
|
admin = fs.String("bootstrap-admin", "", "owner")
|
||||||
|
},
|
||||||
|
AfterParse: func(opt Options) (bool, error) {
|
||||||
|
if admin == nil || *admin == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if opt.DataDir == "" {
|
||||||
|
return true, errors.New("missing data")
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
fs := flag.NewFlagSet("serve", flag.ContinueOnError)
|
||||||
|
RegisterCLIFlags(fs)
|
||||||
|
if err := fs.Parse([]string{"-bootstrap-admin", "owner"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if fs.Lookup("data") != nil {
|
||||||
|
t.Fatal("plugin must not redefine base flags")
|
||||||
|
}
|
||||||
|
handled, err := RunCLIHooks(Options{DataDir: "/tmp/data"})
|
||||||
|
if err != nil || !handled {
|
||||||
|
t.Fatalf("handled=%v err=%v", handled, err)
|
||||||
|
}
|
||||||
|
fs2 := flag.NewFlagSet("serve", flag.ContinueOnError)
|
||||||
|
RegisterCLIFlags(fs2)
|
||||||
|
if err := fs2.Parse(nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
handled, err = RunCLIHooks(Options{DataDir: "/tmp/data"})
|
||||||
|
if err != nil || handled {
|
||||||
|
t.Fatalf("empty flag should not handle: handled=%v err=%v", handled, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ AccountSearcher.SearchAccounts
|
|||||||
AdminProvider
|
AdminProvider
|
||||||
AdminProvider.IsAdmin
|
AdminProvider.IsAdmin
|
||||||
AdminZoneSpec
|
AdminZoneSpec
|
||||||
|
CLIHook
|
||||||
Compiled
|
Compiled
|
||||||
DelegatedProjectProjectionHost
|
DelegatedProjectProjectionHost
|
||||||
DelegatedProjectProjectionHost.ProjectProjectionForCreator
|
DelegatedProjectProjectionHost.ProjectProjectionForCreator
|
||||||
@@ -50,9 +51,11 @@ HostClient
|
|||||||
InProc
|
InProc
|
||||||
IntentSpec
|
IntentSpec
|
||||||
Main
|
Main
|
||||||
|
MergeHostOptions
|
||||||
NewHostClient
|
NewHostClient
|
||||||
NewScopedHost
|
NewScopedHost
|
||||||
Options
|
Options
|
||||||
|
PrepareCompiled
|
||||||
ProjectHost
|
ProjectHost
|
||||||
ProjectHost.EnsureProject
|
ProjectHost.EnsureProject
|
||||||
ProjectHost.OwnedProject
|
ProjectHost.OwnedProject
|
||||||
@@ -64,6 +67,7 @@ ProjectHost.RenameOwnedProject
|
|||||||
ProjectLifecycle
|
ProjectLifecycle
|
||||||
ProjectLifecycleRegistry
|
ProjectLifecycleRegistry
|
||||||
ProjectLifecycleRegistry.RegisterProjectLifecycle
|
ProjectLifecycleRegistry.RegisterProjectLifecycle
|
||||||
|
RegisterCLIFlags
|
||||||
RegisterCompiled
|
RegisterCompiled
|
||||||
Registry
|
Registry
|
||||||
Registry.AddAdminZone
|
Registry.AddAdminZone
|
||||||
@@ -79,6 +83,7 @@ ResetCompiled
|
|||||||
ResourceAuthorizer
|
ResourceAuthorizer
|
||||||
ResourceAuthorizerV2
|
ResourceAuthorizerV2
|
||||||
ResourceAuthzQuery
|
ResourceAuthzQuery
|
||||||
|
RunCLIHooks
|
||||||
SetRunner
|
SetRunner
|
||||||
SetScopedHostFactory
|
SetScopedHostFactory
|
||||||
SurfaceHost
|
SurfaceHost
|
||||||
|
|||||||
Reference in New Issue
Block a user