// Package sandbox — teams_module.go // // Read-only module for querying team role membership from Starlark scripts. // No sandbox permission required — extensions can check roles without special grants. // // Starlark API: // teams.get_member_roles(team_id, user_id) → ["admin", "reviewer", ...] // teams.has_role(team_id, user_id, role) → True/False package sandbox import ( "context" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" "armature/store" ) // BuildTeamsModule creates the "teams" module. // Always available to all extensions (no permission gate). func BuildTeamsModule(ctx context.Context, stores store.Stores) *starlarkstruct.Module { return MakeModule("teams", starlark.StringDict{ "get_member_roles": starlark.NewBuiltin("teams.get_member_roles", func( thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple, ) (starlark.Value, error) { var teamID, userID string if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 2, &teamID, &userID); err != nil { return nil, err } roles, err := stores.Teams.GetMemberRoles(ctx, teamID, userID) if err != nil { return starlark.NewList(nil), nil } elems := make([]starlark.Value, len(roles)) for i, r := range roles { elems[i] = starlark.String(r) } return starlark.NewList(elems), nil }), "has_role": starlark.NewBuiltin("teams.has_role", func( thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple, ) (starlark.Value, error) { var teamID, userID, role string if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 3, &teamID, &userID, &role); err != nil { return nil, err } has, err := stores.Teams.HasRole(ctx, teamID, userID, role) if err != nil || !has { return starlark.False, nil } return starlark.True, nil }), }) }