package app import ( "git-ssh.u1680832.nyat.app/weichen/Agent-System-Backend.git/internal/projectscope" "git-ssh.u1680832.nyat.app/weichen/Agent-System-Backend.git/internal/server" "git-ssh.u1680832.nyat.app/weichen/Agent-System-Backend.git/internal/store" "git-ssh.u1680832.nyat.app/weichen/Agent-System-Backend.git/pkg/extension" core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git" "context" "errors" "sync" ) type projectLifecycleSlot struct { mu sync.Mutex holder string provider *extension.ProjectLifecycle } func (r compiledRegistry) RegisterProjectLifecycle(extension.ProjectLifecycle) error { return errors.New("extension: declarative plugins cannot register project lifecycle") } func (r pluginRegistry) RegisterProjectLifecycle(p extension.ProjectLifecycle) error { if r.caps.projects == nil || r.pluginID == "" { return errors.New("extension: project lifecycle slot unavailable") } if p.List == nil || p.Create == nil || p.Rename == nil || p.Archive == nil || p.Work == nil { return errors.New("extension: project lifecycle requires all callbacks") } slot := r.caps.projects slot.mu.Lock() defer slot.mu.Unlock() if slot.provider != nil { return errors.New("extension: project lifecycle already registered") } slot.holder, slot.provider = r.pluginID, &p return nil } func (r recordRegistry) RegisterProjectLifecycle(p extension.ProjectLifecycle) error { reg, ok := r.Registry.(extension.ProjectLifecycleRegistry) if !ok { return errors.New("extension: project lifecycle registration unavailable") } return reg.RegisterProjectLifecycle(p) } func (slot *projectLifecycleSlot) wire(srv *server.Server, caps hostcaps) { slot.mu.Lock() defer slot.mu.Unlock() if slot.provider == nil { return } holder := slot.holder srv.SetProjectLifecycle(*slot.provider, func(owner string) extension.Host { return inprocHost{caps: caps, owner: owner, actor: owner, pluginID: holder} }) } func (slot *projectLifecycleSlot) active() bool { slot.mu.Lock() defer slot.mu.Unlock() return slot.provider != nil } func (slot *projectLifecycleSlot) work() func(context.Context, string, core.ID) (bool, error) { slot.mu.Lock() defer slot.mu.Unlock() if slot.provider == nil { return nil } return slot.provider.Work } func (slot *projectLifecycleSlot) resolve(ctx context.Context, caps hostcaps, owner string, id core.ID) (core.ID, error) { slot.mu.Lock() if slot.provider == nil { slot.mu.Unlock() return id, nil } p, holder := *slot.provider, slot.holder slot.mu.Unlock() return projectscope.Resolve(ctx, inprocHost{caps: caps, owner: owner, actor: owner, pluginID: holder}, p, id) } var _ extension.ProjectHost = inprocHost{} func (h inprocHost) OwnedProjects() ([]core.Project, error) { ps, err := h.caps.st.ListProjects(h.owner) for i := range ps { ps[i].Owner = h.owner } return ps, err } func (h inprocHost) OwnedProject(id string) (core.Project, error) { return h.caps.st.GetProject(h.owner, id) } func (h inprocHost) ProjectMetadata(id string) (core.Project, error) { p, err := h.caps.st.ProjectMetadata(id) if err != nil { return core.Project{}, err } if h.caps.projects != nil && h.caps.projects.active() { p.Settings = h.caps.set.MaskedProjectMetadata(p.Settings) } if p.Owner == h.owner { return p, nil } if h.caps.authz == nil { return core.Project{}, store.ErrNotFound } ok, err := h.caps.authz.AuthorizeResource(extension.ResourceAuthzQuery{Actor: h.owner, Action: "read", ResourceType: "project", ResourceID: core.ID(id), Owner: p.Owner, ProjectID: core.ID(id)}) if err != nil || !ok { return core.Project{}, store.ErrNotFound } return p, nil } func (h inprocHost) EnsureProject(id, name string) (core.Project, error) { return h.caps.st.EnsureProject(h.owner, id, name) } func (h inprocHost) RenameOwnedProject(id, name string) (core.Project, error) { return h.caps.st.RenameOwnedProject(h.owner, id, name) } func (h inprocHost) ProjectNameAvailable(name, exceptID string) (bool, error) { return h.caps.st.ProjectNameAvailable(name, exceptID) }